001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.util.struct;
006
007import java.nio.BufferUnderflowException;
008import java.nio.ByteBuffer;
009import java.nio.ByteOrder;
010import java.nio.ReadOnlyBufferException;
011import java.nio.charset.StandardCharsets;
012
013/** Dynamic (run-time) access to a serialized raw struct. */
014public final class DynamicStruct {
015  private DynamicStruct(StructDescriptor desc, ByteBuffer data) {
016    m_desc = desc;
017    m_data = data.order(ByteOrder.LITTLE_ENDIAN);
018  }
019
020  /**
021   * Constructs a new dynamic struct object with internal storage. The descriptor must be valid. The
022   * internal storage is allocated using ByteBuffer.allocate().
023   *
024   * @param desc struct descriptor
025   * @return dynamic struct object
026   * @throws IllegalStateException if struct descriptor is invalid
027   */
028  public static DynamicStruct allocate(StructDescriptor desc) {
029    return new DynamicStruct(desc, ByteBuffer.allocate(desc.getSize()));
030  }
031
032  /**
033   * Constructs a new dynamic struct object with internal storage. The descriptor must be valid. The
034   * internal storage is allocated using ByteBuffer.allocateDirect().
035   *
036   * @param desc struct descriptor
037   * @return dynamic struct object
038   * @throws IllegalStateException if struct descriptor is invalid
039   */
040  public static DynamicStruct allocateDirect(StructDescriptor desc) {
041    return new DynamicStruct(desc, ByteBuffer.allocateDirect(desc.getSize()));
042  }
043
044  /**
045   * Constructs a new dynamic struct object. Note: the passed data buffer is not copied.
046   * Modifications to the passed buffer will be reflected in the struct and vice-versa.
047   *
048   * @param desc struct descriptor
049   * @param data byte buffer containing serialized data starting at current position
050   * @return dynamic struct object
051   */
052  public static DynamicStruct wrap(StructDescriptor desc, ByteBuffer data) {
053    return new DynamicStruct(desc, data.slice());
054  }
055
056  /**
057   * Gets the struct descriptor.
058   *
059   * @return struct descriptor
060   */
061  public StructDescriptor getDescriptor() {
062    return m_desc;
063  }
064
065  /**
066   * Gets the serialized backing data buffer.
067   *
068   * @return data buffer
069   */
070  public ByteBuffer getBuffer() {
071    return m_data.duplicate().position(0);
072  }
073
074  /**
075   * Overwrites the entire serialized struct by copying data from a byte array.
076   *
077   * @param data replacement data for the struct
078   * @throws BufferUnderflowException if data is smaller than the struct size
079   * @throws ReadOnlyBufferException if the underlying buffer is read-only
080   * @throws IllegalStateException if struct descriptor is invalid
081   */
082  public void setData(byte[] data) {
083    if (data.length < m_desc.getSize()) {
084      throw new BufferUnderflowException();
085    }
086    m_data.position(0).put(data);
087  }
088
089  /**
090   * Overwrites the entire serialized struct by copying data from a byte buffer.
091   *
092   * @param data replacement data for the struct; copy starts from current position
093   * @throws BufferUnderflowException if remaining data is smaller than the struct size
094   * @throws ReadOnlyBufferException if the underlying buffer is read-only
095   * @throws IllegalStateException if struct descriptor is invalid
096   */
097  public void setData(ByteBuffer data) {
098    if (data.remaining() < m_desc.getSize()) {
099      throw new BufferUnderflowException();
100    }
101    int oldLimit = data.limit();
102    m_data.position(0).put(data.limit(m_desc.getSize()));
103    data.limit(oldLimit);
104  }
105
106  /**
107   * Gets a struct field descriptor by name.
108   *
109   * @param name field name
110   * @return field descriptor, or null if no field with that name exists
111   */
112  public StructFieldDescriptor findField(String name) {
113    return m_desc.findFieldByName(name);
114  }
115
116  /**
117   * Gets the value of a boolean field.
118   *
119   * @param field field descriptor
120   * @param arrIndex array index (must be less than field array size)
121   * @return boolean field value
122   * @throws UnsupportedOperationException if field is not bool type
123   * @throws IllegalArgumentException if field is not a member of this struct
124   * @throws IllegalStateException if struct descriptor is invalid
125   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
126   */
127  public boolean getBoolField(StructFieldDescriptor field, int arrIndex) {
128    if (field.getType() != StructFieldType.kBool) {
129      throw new UnsupportedOperationException("field is not bool type");
130    }
131    return getFieldImpl(field, arrIndex) != 0;
132  }
133
134  /**
135   * Gets the value of a boolean field.
136   *
137   * @param field field descriptor
138   * @return boolean field value
139   * @throws UnsupportedOperationException if field is not bool type
140   * @throws IllegalArgumentException if field is not a member of this struct
141   * @throws IllegalStateException if struct descriptor is invalid
142   */
143  public boolean getBoolField(StructFieldDescriptor field) {
144    return getBoolField(field, 0);
145  }
146
147  /**
148   * Sets the value of a boolean field.
149   *
150   * @param field field descriptor
151   * @param value boolean value
152   * @param arrIndex array index (must be less than field array size)
153   * @throws UnsupportedOperationException if field is not bool type
154   * @throws IllegalArgumentException if field is not a member of this struct
155   * @throws IllegalStateException if struct descriptor is invalid
156   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
157   * @throws ReadOnlyBufferException if the underlying buffer is read-only
158   */
159  public void setBoolField(StructFieldDescriptor field, boolean value, int arrIndex) {
160    if (field.getType() != StructFieldType.kBool) {
161      throw new UnsupportedOperationException("field is not bool type");
162    }
163    setFieldImpl(field, value ? 1 : 0, arrIndex);
164  }
165
166  /**
167   * Sets the value of a boolean field.
168   *
169   * @param field field descriptor
170   * @param value boolean value
171   * @throws UnsupportedOperationException if field is not bool type
172   * @throws IllegalArgumentException if field is not a member of this struct
173   * @throws IllegalStateException if struct descriptor is invalid
174   * @throws ReadOnlyBufferException if the underlying buffer is read-only
175   */
176  public void setBoolField(StructFieldDescriptor field, boolean value) {
177    setBoolField(field, value, 0);
178  }
179
180  /**
181   * Gets the value of an integer field.
182   *
183   * @param field field descriptor
184   * @param arrIndex array index (must be less than field array size)
185   * @return integer field value
186   * @throws UnsupportedOperationException if field is not integer type
187   * @throws IllegalArgumentException if field is not a member of this struct
188   * @throws IllegalStateException if struct descriptor is invalid
189   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
190   */
191  public long getIntField(StructFieldDescriptor field, int arrIndex) {
192    if (!field.isInt() && !field.isUint()) {
193      throw new UnsupportedOperationException("field is not integer type");
194    }
195    return getFieldImpl(field, arrIndex);
196  }
197
198  /**
199   * Gets the value of an integer field.
200   *
201   * @param field field descriptor
202   * @return integer field value
203   * @throws UnsupportedOperationException if field is not integer type
204   * @throws IllegalArgumentException if field is not a member of this struct
205   * @throws IllegalStateException if struct descriptor is invalid
206   */
207  public long getIntField(StructFieldDescriptor field) {
208    return getIntField(field, 0);
209  }
210
211  /**
212   * Sets the value of an integer field.
213   *
214   * @param field field descriptor
215   * @param value integer value
216   * @param arrIndex array index (must be less than field array size)
217   * @throws UnsupportedOperationException if field is not integer type
218   * @throws IllegalArgumentException if field is not a member of this struct
219   * @throws IllegalStateException if struct descriptor is invalid
220   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
221   * @throws ReadOnlyBufferException if the underlying buffer is read-only
222   */
223  public void setIntField(StructFieldDescriptor field, long value, int arrIndex) {
224    if (!field.isInt() && !field.isUint()) {
225      throw new UnsupportedOperationException("field is not integer type");
226    }
227    setFieldImpl(field, value, arrIndex);
228  }
229
230  /**
231   * Sets the value of an integer field.
232   *
233   * @param field field descriptor
234   * @param value integer value
235   * @throws UnsupportedOperationException if field is not integer type
236   * @throws IllegalArgumentException if field is not a member of this struct
237   * @throws IllegalStateException if struct descriptor is invalid
238   * @throws ReadOnlyBufferException if the underlying buffer is read-only
239   */
240  public void setIntField(StructFieldDescriptor field, long value) {
241    setIntField(field, value, 0);
242  }
243
244  /**
245   * Gets the value of a float field.
246   *
247   * @param field field descriptor
248   * @param arrIndex array index (must be less than field array size)
249   * @return float field value
250   * @throws UnsupportedOperationException if field is not float type
251   * @throws IllegalArgumentException if field is not a member of this struct
252   * @throws IllegalStateException if struct descriptor is invalid
253   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
254   */
255  public float getFloatField(StructFieldDescriptor field, int arrIndex) {
256    if (field.getType() != StructFieldType.kFloat) {
257      throw new UnsupportedOperationException("field is not float type");
258    }
259    return Float.intBitsToFloat((int) getFieldImpl(field, arrIndex));
260  }
261
262  /**
263   * Gets the value of a float field.
264   *
265   * @param field field descriptor
266   * @return float field value
267   * @throws UnsupportedOperationException if field is not float type
268   * @throws IllegalArgumentException if field is not a member of this struct
269   * @throws IllegalStateException if struct descriptor is invalid
270   */
271  public float getFloatField(StructFieldDescriptor field) {
272    return getFloatField(field, 0);
273  }
274
275  /**
276   * Sets the value of a float field.
277   *
278   * @param field field descriptor
279   * @param value float value
280   * @param arrIndex array index (must be less than field array size)
281   * @throws UnsupportedOperationException if field is not float type
282   * @throws IllegalArgumentException if field is not a member of this struct
283   * @throws IllegalStateException if struct descriptor is invalid
284   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
285   * @throws ReadOnlyBufferException if the underlying buffer is read-only
286   */
287  public void setFloatField(StructFieldDescriptor field, float value, int arrIndex) {
288    if (field.getType() != StructFieldType.kFloat) {
289      throw new UnsupportedOperationException("field is not float type");
290    }
291    setFieldImpl(field, Float.floatToIntBits(value), arrIndex);
292  }
293
294  /**
295   * Sets the value of a float field.
296   *
297   * @param field field descriptor
298   * @param value float value
299   * @throws UnsupportedOperationException if field is not float type
300   * @throws IllegalArgumentException if field is not a member of this struct
301   * @throws IllegalStateException if struct descriptor is invalid
302   * @throws ReadOnlyBufferException if the underlying buffer is read-only
303   */
304  public void setFloatField(StructFieldDescriptor field, float value) {
305    setFloatField(field, value, 0);
306  }
307
308  /**
309   * Gets the value of a double field.
310   *
311   * @param field field descriptor
312   * @param arrIndex array index (must be less than field array size)
313   * @return double field value
314   * @throws UnsupportedOperationException if field is not double type
315   * @throws IllegalArgumentException if field is not a member of this struct
316   * @throws IllegalStateException if struct descriptor is invalid
317   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
318   */
319  public double getDoubleField(StructFieldDescriptor field, int arrIndex) {
320    if (field.getType() != StructFieldType.kDouble) {
321      throw new UnsupportedOperationException("field is not double type");
322    }
323    return Double.longBitsToDouble(getFieldImpl(field, arrIndex));
324  }
325
326  /**
327   * Gets the value of a double field.
328   *
329   * @param field field descriptor
330   * @return double field value
331   * @throws UnsupportedOperationException if field is not double type
332   * @throws IllegalArgumentException if field is not a member of this struct
333   * @throws IllegalStateException if struct descriptor is invalid
334   */
335  public double getDoubleField(StructFieldDescriptor field) {
336    return getDoubleField(field, 0);
337  }
338
339  /**
340   * Sets the value of a double field.
341   *
342   * @param field field descriptor
343   * @param value double value
344   * @param arrIndex array index (must be less than field array size)
345   * @throws UnsupportedOperationException if field is not double type
346   * @throws IllegalArgumentException if field is not a member of this struct
347   * @throws IllegalStateException if struct descriptor is invalid
348   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
349   * @throws ReadOnlyBufferException if the underlying buffer is read-only
350   */
351  public void setDoubleField(StructFieldDescriptor field, double value, int arrIndex) {
352    if (field.getType() != StructFieldType.kDouble) {
353      throw new UnsupportedOperationException("field is not double type");
354    }
355    setFieldImpl(field, Double.doubleToLongBits(value), arrIndex);
356  }
357
358  /**
359   * Sets the value of a double field.
360   *
361   * @param field field descriptor
362   * @param value double value
363   * @throws UnsupportedOperationException if field is not double type
364   * @throws IllegalArgumentException if field is not a member of this struct
365   * @throws IllegalStateException if struct descriptor is invalid
366   * @throws ReadOnlyBufferException if the underlying buffer is read-only
367   */
368  public void setDoubleField(StructFieldDescriptor field, double value) {
369    setDoubleField(field, value, 0);
370  }
371
372  /**
373   * Gets the value of a character or character array field.
374   *
375   * @param field field descriptor
376   * @return field value
377   * @throws UnsupportedOperationException if field is not char type
378   * @throws IllegalArgumentException if field is not a member of this struct
379   * @throws IllegalStateException if struct descriptor is invalid
380   */
381  @SuppressWarnings({"PMD.CollapsibleIfStatements", "PMD.AvoidDeeplyNestedIfStmts"})
382  public String getStringField(StructFieldDescriptor field) {
383    if (field.getType() != StructFieldType.kChar) {
384      throw new UnsupportedOperationException("field is not char type");
385    }
386    if (!field.getParent().equals(m_desc)) {
387      throw new IllegalArgumentException("field is not part of this struct");
388    }
389    if (!m_desc.isValid()) {
390      throw new IllegalStateException("struct descriptor is not valid");
391    }
392    byte[] bytes = new byte[field.m_arraySize];
393    m_data.position(field.m_offset).get(bytes, 0, field.m_arraySize);
394    // Find last non zero character
395    int stringLength = bytes.length;
396    for (; stringLength > 0; stringLength--) {
397      if (bytes[stringLength - 1] != 0) {
398        break;
399      }
400    }
401    // If string is all zeroes, its empty and return an empty string.
402    if (stringLength == 0) {
403      return "";
404    }
405    // Check if the end of the string is in the middle of a continuation byte or
406    // not.
407    if ((bytes[stringLength - 1] & 0x80) != 0) {
408      // This is a UTF8 continuation byte. Make sure its valid.
409      // Walk back until initial byte is found
410      int utf8StartByte = stringLength;
411      for (; utf8StartByte > 0; utf8StartByte--) {
412        if ((bytes[utf8StartByte - 1] & 0x40) != 0) {
413          // Having 2nd bit set means start byte
414          break;
415        }
416      }
417      if (utf8StartByte == 0) {
418        // This case means string only contains continuation bytes
419        return "";
420      }
421      utf8StartByte--;
422      // Check if its a 2, 3, or 4 byte
423      byte checkByte = bytes[utf8StartByte];
424      if ((checkByte & 0xE0) == 0xC0) {
425        // 2 byte, need 1 more byte
426        if (utf8StartByte != stringLength - 2) {
427          stringLength = utf8StartByte;
428        }
429      } else if ((checkByte & 0xF0) == 0xE0) {
430        // 3 byte, need 2 more bytes
431        if (utf8StartByte != stringLength - 3) {
432          stringLength = utf8StartByte;
433        }
434      } else if ((checkByte & 0xF8) == 0xF0) {
435        // 4 byte, need 3 more bytes
436        if (utf8StartByte != stringLength - 4) {
437          stringLength = utf8StartByte;
438        }
439      }
440      // If we get here, the string is either completely garbage or fine.
441    }
442
443    return new String(bytes, 0, stringLength, StandardCharsets.UTF_8);
444  }
445
446  /**
447   * Sets the value of a character or character array field.
448   *
449   * @param field field descriptor
450   * @param value field value
451   * @return true if the full value fit in the struct, false if truncated
452   * @throws UnsupportedOperationException if field is not char type
453   * @throws IllegalArgumentException if field is not a member of this struct
454   * @throws IllegalStateException if struct descriptor is invalid
455   */
456  public boolean setStringField(StructFieldDescriptor field, String value) {
457    if (field.getType() != StructFieldType.kChar) {
458      throw new UnsupportedOperationException("field is not char type");
459    }
460    if (!field.getParent().equals(m_desc)) {
461      throw new IllegalArgumentException("field is not part of this struct");
462    }
463    if (!m_desc.isValid()) {
464      throw new IllegalStateException("struct descriptor is not valid");
465    }
466    ByteBuffer bb = StandardCharsets.UTF_8.encode(value);
467    int len = Math.min(bb.remaining(), field.m_arraySize);
468    boolean copiedFull = len == bb.remaining();
469    m_data.position(field.m_offset).put(bb.limit(len));
470    for (int i = len; i < field.m_arraySize; i++) {
471      m_data.put((byte) 0);
472    }
473    return copiedFull;
474  }
475
476  /**
477   * Gets the value of a struct field.
478   *
479   * @param field field descriptor
480   * @param arrIndex array index (must be less than field array size)
481   * @return field value
482   * @throws UnsupportedOperationException if field is not of struct type
483   * @throws IllegalArgumentException if field is not a member of this struct
484   * @throws IllegalStateException if struct descriptor is invalid
485   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
486   */
487  public DynamicStruct getStructField(StructFieldDescriptor field, int arrIndex) {
488    if (field.getType() != StructFieldType.kStruct) {
489      throw new UnsupportedOperationException("field is not struct type");
490    }
491    if (!field.getParent().equals(m_desc)) {
492      throw new IllegalArgumentException("field is not part of this struct");
493    }
494    if (!m_desc.isValid()) {
495      throw new IllegalStateException("struct descriptor is not valid");
496    }
497    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
498      throw new ArrayIndexOutOfBoundsException(
499          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
500    }
501    StructDescriptor struct = field.getStruct();
502    return wrap(struct, m_data.position(field.m_offset + arrIndex * struct.m_size));
503  }
504
505  /**
506   * Gets the value of a struct field.
507   *
508   * @param field field descriptor
509   * @return field value
510   * @throws UnsupportedOperationException if field is not of struct type
511   * @throws IllegalArgumentException if field is not a member of this struct
512   * @throws IllegalStateException if struct descriptor is invalid
513   */
514  public DynamicStruct getStructField(StructFieldDescriptor field) {
515    return getStructField(field, 0);
516  }
517
518  /**
519   * Sets the value of a struct field.
520   *
521   * @param field field descriptor
522   * @param value struct value
523   * @param arrIndex array index (must be less than field array size)
524   * @throws UnsupportedOperationException if field is not struct type
525   * @throws IllegalArgumentException if field is not a member of this struct
526   * @throws IllegalStateException if struct descriptor is invalid
527   * @throws ArrayIndexOutOfBoundsException if array index is out of bounds
528   * @throws ReadOnlyBufferException if the underlying buffer is read-only
529   */
530  public void setStructField(StructFieldDescriptor field, DynamicStruct value, int arrIndex) {
531    if (field.getType() != StructFieldType.kStruct) {
532      throw new UnsupportedOperationException("field is not struct type");
533    }
534    if (!field.getParent().equals(m_desc)) {
535      throw new IllegalArgumentException("field is not part of this struct");
536    }
537    if (!m_desc.isValid()) {
538      throw new IllegalStateException("struct descriptor is not valid");
539    }
540    StructDescriptor struct = field.getStruct();
541    if (!value.getDescriptor().equals(struct)) {
542      throw new IllegalArgumentException("value's struct type does not match field struct type");
543    }
544    if (!value.getDescriptor().isValid()) {
545      throw new IllegalStateException("value's struct descriptor is not valid");
546    }
547    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
548      throw new ArrayIndexOutOfBoundsException(
549          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
550    }
551    m_data
552        .position(field.m_offset + arrIndex * struct.m_size)
553        .put(value.m_data.position(0).limit(value.getDescriptor().getSize()));
554  }
555
556  /**
557   * Sets the value of a struct field.
558   *
559   * @param field field descriptor
560   * @param value struct value
561   * @throws UnsupportedOperationException if field is not struct type
562   * @throws IllegalArgumentException if field is not a member of this struct
563   * @throws IllegalStateException if struct descriptor is invalid
564   * @throws ReadOnlyBufferException if the underlying buffer is read-only
565   */
566  public void setStructField(StructFieldDescriptor field, DynamicStruct value) {
567    setStructField(field, value, 0);
568  }
569
570  private long getFieldImpl(StructFieldDescriptor field, int arrIndex) {
571    if (!field.getParent().equals(m_desc)) {
572      throw new IllegalArgumentException("field is not part of this struct");
573    }
574    if (!m_desc.isValid()) {
575      throw new IllegalStateException("struct descriptor is not valid");
576    }
577    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
578      throw new ArrayIndexOutOfBoundsException(
579          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
580    }
581
582    long val;
583    switch (field.m_size) {
584      case 1:
585        val = m_data.get(field.m_offset + arrIndex);
586        break;
587      case 2:
588        val = m_data.getShort(field.m_offset + arrIndex * 2);
589        break;
590      case 4:
591        val = m_data.getInt(field.m_offset + arrIndex * 4);
592        break;
593      case 8:
594        val = m_data.getLong(field.m_offset + arrIndex * 8);
595        break;
596      default:
597        throw new IllegalStateException("invalid field size");
598    }
599
600    if (field.isUint() || field.getType() == StructFieldType.kBool) {
601      // for unsigned fields, we can simply logical shift and mask
602      return (val >>> field.m_bitShift) & field.getBitMask();
603    } else {
604      // to get sign extension, shift so the sign bit within the bitfield goes to the long's sign
605      // bit (also clearing all higher bits), then shift back down (also clearing all lower bits);
606      // since upper and lower bits are cleared with the shifts, the bitmask is unnecessary
607      return (val << (64 - field.m_bitShift - field.getBitWidth())) >> (64 - field.getBitWidth());
608    }
609  }
610
611  private void setFieldImpl(StructFieldDescriptor field, long value, int arrIndex) {
612    if (!field.getParent().equals(m_desc)) {
613      throw new IllegalArgumentException("field is not part of this struct");
614    }
615    if (!m_desc.isValid()) {
616      throw new IllegalStateException("struct descriptor is not valid");
617    }
618    if (arrIndex < 0 || arrIndex >= field.m_arraySize) {
619      throw new ArrayIndexOutOfBoundsException(
620          "arrIndex (" + arrIndex + ") is larger than array size (" + field.m_arraySize + ")");
621    }
622
623    // common case is no bit shift and no masking
624    if (!field.isBitField()) {
625      switch (field.m_size) {
626        case 1:
627          m_data.put(field.m_offset + arrIndex, (byte) value);
628          break;
629        case 2:
630          m_data.putShort(field.m_offset + arrIndex * 2, (short) value);
631          break;
632        case 4:
633          m_data.putInt(field.m_offset + arrIndex * 4, (int) value);
634          break;
635        case 8:
636          m_data.putLong(field.m_offset + arrIndex * 8, value);
637          break;
638        default:
639          throw new IllegalStateException("invalid field size");
640      }
641      return;
642    }
643
644    // handle bit shifting and masking into current value
645    switch (field.m_size) {
646      case 1:
647        {
648          byte val = m_data.get(field.m_offset + arrIndex);
649          val &= (byte) ~(field.getBitMask() << field.m_bitShift);
650          val |= (byte) ((value & field.getBitMask()) << field.m_bitShift);
651          m_data.put(field.m_offset + arrIndex, val);
652          break;
653        }
654      case 2:
655        {
656          short val = m_data.getShort(field.m_offset + arrIndex * 2);
657          val &= (short) ~(field.getBitMask() << field.m_bitShift);
658          val |= (short) ((value & field.getBitMask()) << field.m_bitShift);
659          m_data.putShort(field.m_offset + arrIndex * 2, val);
660          break;
661        }
662      case 4:
663        {
664          int val = m_data.getInt(field.m_offset + arrIndex * 4);
665          val &= (int) ~(field.getBitMask() << field.m_bitShift);
666          val |= (int) ((value & field.getBitMask()) << field.m_bitShift);
667          m_data.putInt(field.m_offset + arrIndex * 4, val);
668          break;
669        }
670      case 8:
671        {
672          long val = m_data.getLong(field.m_offset + arrIndex * 8);
673          val &= ~(field.getBitMask() << field.m_bitShift);
674          val |= (value & field.getBitMask()) << field.m_bitShift;
675          m_data.putLong(field.m_offset + arrIndex * 8, val);
676          break;
677        }
678      default:
679        throw new IllegalStateException("invalid field size");
680    }
681  }
682
683  private final StructDescriptor m_desc;
684  private final ByteBuffer m_data;
685}