1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package uk.ac.rdg.resc.jstyx.messages;
30
31 import org.apache.mina.common.ByteBuffer;
32 import java.nio.ByteOrder;
33
34 import uk.ac.rdg.resc.jstyx.StyxUtils;
35 import uk.ac.rdg.resc.jstyx.types.Qid;
36 import uk.ac.rdg.resc.jstyx.types.ULong;
37 import uk.ac.rdg.resc.jstyx.types.DirEntry;
38
39 /***
40 * Wrapper class for a ByteBuffer that allows Styx primitives to be easily
41 * read from and written to the buffer. Reads and writes unsigned bytes, shorts
42 * and integers, in addition to other Styx types.
43 * Before using the methods in this class, you should check that the required
44 * number of bytes are available in the ByteBuffer. The easiest way to do this
45 * is to read the message length (the first four bytes in the Styx message) and
46 * check that this number of bytes are available in the buffer.
47 * @todo do these functions need to be thread-safe? Are two different threads
48 * ever going to want to access the same buffer simultaneously?
49 * @todo does this belong in the messages package?
50 *
51 * @author Jon Blower
52 * $Revision: 507 $
53 * $Date: 2005-12-01 08:21:56 +0000 (Thu, 01 Dec 2005) $
54 * $Log$
55 * Revision 1.5 2005/12/01 08:21:56 jonblower
56 * Fixed javadoc comments
57 *
58 * Revision 1.4 2005/11/03 07:46:55 jonblower
59 * Trying to fix bug with sending RreadMessages
60 *
61 * Revision 1.3 2005/03/16 17:56:22 jonblower
62 * Replaced use of java.nio.ByteBuffer with MINA's ByteBuffer to minimise copying of buffers
63 *
64 * Revision 1.2 2005/03/11 14:02:16 jonblower
65 * Merged MINA-Test_20059309 into main line of development
66 *
67 * Revision 1.1.2.1 2005/03/10 11:35:28 jonblower
68 * Moved from JStyx root package
69 *
70 * Revision 1.2 2005/03/09 17:01:23 jonblower
71 * Added more methods reflecting methods in underlying ByteBuffer
72 *
73 * Revision 1.1.1.1 2005/02/16 18:58:16 jonblower
74 * Initial import
75 *
76 */
77 public class StyxBuffer
78 {
79
80 private ByteBuffer buf;
81
82 /***
83 * Creates a new instance of StyxBuffer
84 * @param buf The ByteBuffer to wrap
85 * @throws IllegalArgumentException if the ByteBuffer is null
86 */
87 public StyxBuffer(ByteBuffer buf)
88 {
89 if (buf == null)
90 {
91 throw new IllegalArgumentException("Byte buffer cannot be null");
92 }
93 this.buf = buf;
94
95 this.buf.order(ByteOrder.LITTLE_ENDIAN);
96 }
97
98 /***
99 * @return the next unsigned byte as an integer between 0 and 255
100 */
101 public int getUByte()
102 {
103 byte b = buf.get();
104 return b & 0xff;
105 }
106
107 /***
108 * Puts an unsigned byte into the buffer at the current position
109 * @param b The value of the byte as an integer between 0 and 255
110 * @return this StyxBuffer (allows chaining of put commands)
111 * @throws IllegalArgumentException if the parameter is out of range
112 */
113 public StyxBuffer putUByte(int b)
114 {
115 if (b < 0 || b > StyxUtils.MAXUBYTE)
116 {
117 throw new IllegalArgumentException("Value (" + b + ") out of range of UByte (0-255)");
118 }
119 buf.put((byte)b);
120 return this;
121 }
122
123 /***
124 * @return the next unsigned short (2 bytes) as an integer between 0 and 65335
125 */
126 public int getUShort()
127 {
128 short s = buf.getShort();
129 return s & 0xffff;
130 }
131
132 /***
133 * Puts an unsigned short (2 bytes) into the buffer at the current position
134 * @param s The value of the short as an integer between 0 and 65535
135 * @return this StyxBuffer (allows chaining of put commands)
136 * @throws IllegalArgumentException if the parameter is out of range
137 */
138 public StyxBuffer putUShort(int s)
139 {
140 if (s < 0 || s > StyxUtils.MAXUSHORT)
141 {
142 throw new IllegalArgumentException("Value (" + s +
143 ") out of range of UShort (0-" + StyxUtils.MAXUSHORT + ")");
144 }
145 buf.putShort((short)s);
146 return this;
147 }
148
149 /***
150 * Puts an unsigned short (2 bytes) into the buffer at the given position
151 * @param index The place in the buffer at which to put the data
152 * @param s The value of the short as an integer between 0 and 65535
153 * @return this StyxBuffer (allows chaining of put commands)
154 * @throws IllegalArgumentException if the parameter is out of range
155 */
156 public StyxBuffer putUShort(int index, int s)
157 {
158 if (s < 0 || s > StyxUtils.MAXUSHORT)
159 {
160 throw new IllegalArgumentException("Value (" + s +
161 ") out of range of UShort (0-" + StyxUtils.MAXUSHORT + ")");
162 }
163 buf.putShort(index, (short)s);
164 return this;
165 }
166
167 /***
168 * @return the next unsigned int (4 bytes) as a long between 0 and 4,294,967,295
169 */
170 public long getUInt()
171 {
172 int i = buf.getInt();
173 return i & 0xffffffffL;
174 }
175
176 /***
177 * Puts an unsigned int (4 bytes) into the buffer at the current position
178 * @param l The value of the int as a long integer between 0 and 4,294,967,295
179 * @return this StyxBuffer (allows chaining of put commands)
180 * @throws IllegalArgumentException if the parameter is out of range
181 */
182 public StyxBuffer putUInt(long l)
183 {
184 if (l < 0 || l > StyxUtils.MAXUINT)
185 {
186 throw new IllegalArgumentException("Value (" + l + ") out of range of UInt (0-4294967295)");
187 }
188 buf.putInt((int)l);
189 return this;
190 }
191
192 /***
193 * Puts an unsigned int (4 bytes) into the buffer at the given position
194 * @param index The place in the buffer at which to put the data
195 * @param l The value of the int as a long integer between 0 and 4,294,967,295
196 * @return this StyxBuffer (allows chaining of put commands)
197 * @throws IllegalArgumentException if the parameter is out of range
198 */
199 public StyxBuffer putUInt(int index, long l)
200 {
201 if (l < 0 || l > StyxUtils.MAXUINT)
202 {
203 throw new IllegalArgumentException("Value (" + l + ") out of range of UInt (0-4294967295)");
204 }
205 buf.putInt(index, (int)l);
206 return this;
207 }
208
209 /***
210 * @return the next ULong (unsigned 8-byte integer)
211 */
212 public ULong getULong()
213 {
214 byte[] bytes = new byte[8];
215 buf.get(bytes);
216 return new ULong(bytes);
217 }
218
219 /***
220 * Puts the given ULong to the buffer at the current position
221 * @param ulong The ulong to write
222 * @return this StyxBuffer (allows chaining of put commands)
223 */
224 public StyxBuffer putULong(ULong ulong)
225 {
226 byte[] bytes = ulong.getBytes();
227 buf.put(bytes);
228 return this;
229 }
230
231 /***
232 * @return the next string
233 */
234 public String getString()
235 {
236
237 int strLen = this.getUShort();
238
239
240 byte[] bytes = new byte[strLen];
241
242 buf.get(bytes);
243 return StyxUtils.utf8ToString(bytes);
244 }
245
246 /***
247 * Puts a string into the buffer at the current position. First converts
248 * the string to an array of bytes in UTF-8.
249 * @param s The string to write to the buffer
250 * @return this StyxBuffer (allows chaining of put commands)
251 */
252 public StyxBuffer putString(String s)
253 {
254 byte[] bytes = StyxUtils.strToUTF8(s);
255 this.putUShort(bytes.length);
256 buf.put(bytes);
257 return this;
258 }
259
260 /***
261 * @return The next Qid (server's representation of a file)
262 */
263 public Qid getQid()
264 {
265 int type = this.getUByte();
266 long version = this.getUInt();
267
268 byte[] pathBytes = new byte[8];
269 buf.get(pathBytes);
270 return new Qid(type, version, new ULong(pathBytes));
271 }
272
273 /***
274 * Puts the given Qid to the buffer at the current position
275 * @param qid The Qid to write
276 * @return this StyxBuffer (allows chaining of put commands)
277 */
278 public StyxBuffer putQid(Qid qid)
279 {
280 this.putUByte(qid.getType()).putUInt(qid.getVersion()).putULong(qid.getPath());
281 return this;
282 }
283
284 /***
285 * @return The next DirEntry (Directory entry)
286 */
287 public DirEntry getDirEntry()
288 {
289 int dirEntrySize = this.getUShort();
290 int type = this.getUShort();
291 long dev = this.getUInt();
292 Qid qid = this.getQid();
293 long mode = this.getUInt();
294 long lastAccessTime = this.getUInt();
295 long lastModTime = this.getUInt();
296 ULong fileLength = this.getULong();
297 String fileName = this.getString();
298 String uid = this.getString();
299 String gid = this.getString();
300 String lastModifiedBy = this.getString();
301
302 return new DirEntry(type, dev, qid, mode, lastAccessTime,
303 lastModTime, fileLength, fileName, uid, gid, lastModifiedBy);
304 }
305
306 /***
307 * Puts the given DirEntry to the buffer at the current position
308 * @param dir The DirEntry to write
309 * @return this StyxBuffer (allows chaining of put commands)
310 */
311 public StyxBuffer putDirEntry(DirEntry dir)
312 {
313 this.putUShort(dir.getSize() - 2);
314 this.putUShort(dir.getType());
315 this.putUInt(dir.getDev());
316 this.putQid(dir.getQid());
317 this.putUInt(dir.getMode());
318 this.putUInt(dir.getLastAccessTime());
319 this.putUInt(dir.getLastModifiedTime());
320 this.putULong(dir.getFileLength());
321 this.putString(dir.getFileName());
322 this.putString(dir.getOwner());
323 this.putString(dir.getGroup());
324 this.putString(dir.getLastModifiedBy());
325
326 return this;
327 }
328
329 /***
330 * Gets a chunk of data from the buffer. After calling this method
331 * successfully, the position of the buffer will be increased by <code>size</code>.
332 * @param size the number of bytes to get
333 * @return A new byte array containing a <i>copy</i> of the data in this buffer
334 * @throws IllegalArgumentException if there aren't enough bytes left in the buffer
335 */
336 public byte[] getData(int size)
337 {
338 if (this.buf.remaining() < size)
339 {
340 throw new IllegalArgumentException("There are fewer than " + size
341 + " bytes remaining in the buffer");
342 }
343
344 byte[] bytes = new byte[size];
345 this.buf.get(bytes);
346 return bytes;
347 }
348
349 public StyxBuffer put(byte[] bytes)
350 {
351 this.buf.put(bytes);
352 return this;
353 }
354
355 public StyxBuffer put(byte[] bytes, int offset, int length)
356 {
357 this.buf.put(bytes, offset, length);
358 return this;
359 }
360
361 /***
362 * Puts a chunk of data to the buffer.
363 * @param data The data to write. The position and limit of this buffer will
364 * be unaffected by this method.
365 * @param size The number of bytes to write
366 * @return this StyxBuffer (allows chaining of put commands)
367 * @throws IllegalArgumentException if the buffer does not contain at least
368 * <code>size</code> bytes, or if there is not enough space in the output
369 * buffer for the data.
370 */
371 public StyxBuffer putData(ByteBuffer data, long size)
372 {
373 if (data.remaining() < size)
374 {
375 throw new IllegalArgumentException("There are fewer than " + size +
376 " bytes remaining in the input data buffer.");
377 }
378 if (this.buf.remaining() < size)
379 {
380 throw new IllegalArgumentException("There is not enough space for "
381 + size + " bytes in the output buffer.");
382 }
383
384 int pos = data.position();
385 int limit = data.limit();
386
387 data.limit(data.position() + (int)size);
388
389
390 this.buf.put(data);
391
392 data.position(pos);
393 data.limit(limit);
394
395 return this;
396 }
397
398
399
400 /***
401 * Puts a chunk of data to the buffer.
402 * @param data The data to write. The position and limit of this buffer will
403 * be unaffected by this method. All the remaining data in the buffer will
404 * be written.
405 * @return this StyxBuffer (allows chaining of put commands)
406 */
407 public StyxBuffer putData(ByteBuffer data)
408 {
409 return this.putData(data, data.remaining());
410 }
411
412 /***
413 * Gets the underlying ByteBuffer
414 */
415 public ByteBuffer getBuffer()
416 {
417 return this.buf;
418 }
419
420 /***
421 * @return the number of bytes remaining in the underlying buffer
422 */
423 public int remaining()
424 {
425 return this.buf.remaining();
426 }
427
428 /***
429 * Flip the underlying buffer
430 */
431 public void flip()
432 {
433 this.buf.flip();
434 }
435 }