001 /* 002 * Copyright (c) 2003, The Regents of the University of California, through 003 * Lawrence Berkeley National Laboratory (subject to receipt of any required 004 * approvals from the U.S. Dept. of Energy). All rights reserved. 005 */ 006 package gov.lbl.dsd.sea.nio.util; 007 008 import java.nio.Buffer; 009 import java.nio.ByteBuffer; 010 import java.nio.CharBuffer; 011 import java.nio.InvalidMarkException; 012 import java.nio.charset.Charset; 013 014 /** 015 * Various utilities related to the {@link java.nio.ByteBuffer} buffers; in particular 016 * String conversions using a {@link Charset}. 017 * 018 * @author whoschek@lbl.gov 019 * @author $Author: hoschek3 $ 020 * @version $Revision: 1.5 $, $Date: 2004/06/01 20:47:20 $ 021 */ 022 public class BufferUtil { 023 024 private BufferUtil() {} // not instantiable 025 026 /** 027 * Ensures that a given buffer can hold up to <tt>minCapacity</tt> 028 * bytes. 029 * 030 * Returns the identical buffer if it can hold at least the number of 031 * bytes specified. Otherwise, returns a new buffer with increased 032 * capacity containing the same bytes, ensuring that it can hold at least 033 * the number of elements specified by the minimum capacity argument. 034 * The new buffer will have a limit equal to the new capacity. 035 * 036 * Leaves the position and byte order unmodified. 037 * 038 * @param minCapacity 039 * the desired minimum capacity. 040 */ 041 public static ByteBuffer ensureCapacity(ByteBuffer buffer, int minCapacity) { 042 int oldCapacity = buffer.capacity(); 043 if (minCapacity > oldCapacity) { 044 int newCapacity = (oldCapacity * 3) / 2 + 1; 045 if (newCapacity < minCapacity) { 046 newCapacity = minCapacity; 047 } 048 049 ByteBuffer newBuffer = buffer.isDirect() ? ByteBuffer.allocateDirect(newCapacity) : ByteBuffer.allocate(newCapacity); 050 newBuffer.order(buffer.order()); 051 int pos = buffer.position(); 052 buffer.position(0); 053 054 newBuffer.put(buffer); 055 056 buffer.position(pos); 057 newBuffer.position(pos); 058 //newBuffer.limit(buffer.limit()); 059 return newBuffer; 060 } else { 061 return buffer; 062 } 063 } 064 065 /** 066 * Creates and returns a new buffer containing the remaining bytes in the 067 * given buffers; Leaves the input buffers unmodified. 068 * 069 * @param buffers - 070 * the buffers to concatenate 071 * @return the new buffer, ready to read from 072 */ 073 public static ByteBuffer concat(ByteBuffer[] buffers) { 074 int n = 0; 075 for (int i = 0; i < buffers.length; i++) 076 n += buffers[i].remaining(); 077 078 ByteBuffer buf = (n > 0 && buffers[0].isDirect()) ? ByteBuffer 079 .allocateDirect(n) : ByteBuffer.allocate(n); 080 if (n > 0) buf.order(buffers[0].order()); 081 082 for (int i = 0; i < buffers.length; i++) 083 buf.put(buffers[i].duplicate()); 084 085 buf.flip(); 086 return buf; 087 } 088 089 /** 090 * The buffer equivalent of {@link System#arraycopy}; 091 * copies src[srcPos]..src[srcPos+length-1] to dest[destPos]..dest[destPos+length-1], 092 * leaving the mark, position and limit of both src and dest unmodified; 093 * Behaves as expected even if src==dest. 094 * 095 * @param src the buffer to read from 096 * @param srcPos the index of the first byte to read 097 * @param dest the buffer to write to 098 * @param destPos the index of the first byte to write 099 * @param length the number of bytes to copy 100 */ 101 public static void copy(ByteBuffer src, int srcPos, ByteBuffer dest, int destPos, int length) { 102 if (length == 0) return; 103 src = src.duplicate(); 104 src.position(srcPos); 105 src.limit(srcPos + length); 106 107 dest = dest.duplicate(); 108 dest.position(destPos); 109 dest.limit(destPos + length); 110 111 dest.put(src); 112 } 113 114 /** 115 * Creates a new buffer that is a deep copy of the remaining bytes in the 116 * given buffer (between index buf.position() and buf.limit()); leaves the 117 * src buffer unmodified. High performance implementation. 118 * 119 * @param src 120 * the buffer to copy 121 * @return the new buffer, ready to read from 122 */ 123 public static ByteBuffer copy(ByteBuffer src) { 124 ByteBuffer copy = src.isDirect() ? ByteBuffer 125 .allocateDirect(src.remaining()) : ByteBuffer.allocate(src.remaining()); 126 copy.order(src.order()); 127 copy.put(src.duplicate()); 128 copy.flip(); 129 return copy; 130 131 // src = src.duplicate(); // leave unmodified 132 // ByteBuffer copy = ByteBuffer.allocate(src.position()); 133 // src.flip(); 134 // copy.put(src); 135 // return copy; 136 } 137 138 /** 139 * Returns a string holding a decoded representation of the remaining bytes 140 * in the given buffer (relative bulk operation). 141 * 142 * @param buffer 143 * the buffer to convert. 144 * @param charset the requested charset to convert with 145 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 146 * @return the string 147 */ 148 public static String getString(ByteBuffer buffer, Charset charset) { 149 return charset.decode(buffer).toString(); 150 } 151 152 /** 153 * Returns whether or not a given buffer has the mark defined. 154 * @param buffer the buffer to check 155 * @return true if it has a mark 156 */ 157 public static boolean hasMark(Buffer buffer) { 158 // unfortunately this seems to be the only way to figure it out :-( 159 boolean hasMark = true; 160 int pos = buffer.position(); 161 try { 162 buffer.reset(); 163 buffer.position(pos); 164 } 165 catch (InvalidMarkException e) { 166 hasMark = false; 167 } 168 return hasMark; 169 } 170 171 /** 172 * Fills the bytes of the encoded string into the given buffer (relative 173 * bulk operation). 174 * 175 * @param buffer 176 * the buffer to fill into. 177 * @param str 178 * the string to convert. 179 * @param charset the requested charset to convert with 180 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 181 */ 182 public static void putString(ByteBuffer buffer, String str, Charset charset) { 183 charset.newEncoder().encode(CharBuffer.wrap(str), buffer, true); 184 185 // inefficient, would allocate temporary buffer memory: 186 // buffer.put(toByteBuffer(str, charset)); 187 } 188 189 /** 190 * Creates and returns a byte array filled with the remaining bytes of given 191 * buffer (between index buf.position() and buf.limit()); leaves the src 192 * buffer unmodified. High performance implementation. 193 * 194 * @param src 195 * the bytebuffer to read from 196 * @return the byte array 197 */ 198 public static byte[] toByteArray(ByteBuffer src) { 199 byte[] copy = new byte[src.remaining()]; 200 src.duplicate().get(copy); 201 return copy; 202 203 // src = src.duplicate(); // leave unmodified 204 // byte[] copy = new byte[src.position()]; 205 // src.flip(); 206 // src.get(copy); 207 // return copy; 208 } 209 210 /** 211 * Returns a bytebuffer holding the encoded bytes of the given string. 212 * 213 * @param str the string to convert. 214 * @param charset the requested charset to convert with 215 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 216 * @return the byte buffer 217 */ 218 public static ByteBuffer toByteBuffer(String str, Charset charset) { 219 return charset.encode(CharBuffer.wrap(str)); 220 } 221 222 /** 223 * Returns a byte array holding the encoded bytes of the given string. 224 * 225 * @param str the string to convert. 226 * @param charset the requested charset to convert with 227 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 228 * @return the byte array 229 */ 230 public static byte[] toByteArray(String str, Charset charset) { 231 return toByteArray(toByteBuffer(str, charset)); 232 } 233 234 /** 235 * Returns a decoded string representation of the bytes in the given buffer; 236 * leaves the buffer unmodified. 237 * 238 * @param buffer the buffer to convert 239 * @param charset the requested charset to convert with 240 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 241 * @return the string representation 242 */ 243 public static String toString(ByteBuffer buffer, Charset charset) { 244 return charset.decode(buffer.duplicate()).toString(); 245 } 246 247 /** 248 * Returns a decoded string representation of the given bytes. 249 * 250 * @param bytes 251 * the bytes to convert 252 * @param offset 253 * The offset of the subarray to be used; must be non-negative 254 * and no larger than <tt>bytes</tt>. 255 * @param length 256 * The length of the subarray to be used; must be non-negative 257 * and no larger than <tt>bytes.length - offset</tt>. 258 * @param charset 259 * the requested charset to convert with (e.g. 260 * Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 261 * @return the string representation 262 */ 263 public static String toString(byte[] bytes, int offset, int length, Charset charset) { 264 return toString(ByteBuffer.wrap(bytes, offset, length), charset); 265 } 266 267 /** 268 * Returns a decoded string representation of the given bytes. 269 * 270 * @param bytes the bytes to convert 271 * @param charset the requested charset to convert with 272 * (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8")) 273 * @return the string representation 274 */ 275 public static String toString(byte[] bytes, Charset charset) { 276 return toString(bytes, 0, bytes.length, charset); 277 } 278 279 // public static void main(String[] args) { 280 // int runs = Integer.parseInt(args[0]); 281 // ByteBuffer buf = ByteBuffer.allocate(1); 282 // buf.mark(); 283 // System.out.println("hasMark = "+ hasMark(buf)); 284 // long start = System.currentTimeMillis(); 285 // int k=0; 286 // for (int i=0; i < runs; i++) { 287 // if (hasMark(buf)) k++; 288 // } 289 // long time = System.currentTimeMillis() - start; 290 // System.out.println("time = "+ (time / 1000.0f)); 291 // System.out.println("iters/s = "+ runs / (time / 1000.0f)); 292 // System.out.println(k); 293 //} 294 295 } 296