@Override public Object next() { if (!hasNext()) { throw new NoSuchElementException(); } DataType<?> t = types[idx++]; if (src.getPosition() == src.getLength() && t.isNullable()) { return null; } return t.decode(src); }
@SuppressWarnings("unchecked") @Override public int encode(PositionedByteRange dst, Object[] val) { if (val.length == 0) { return 0; } assert fields.length >= val.length; int end, written = 0; // find the last occurrence of a non-null or null and non-nullable value for (end = val.length - 1; end > -1; end--) { if (null != val[end] || (null == val[end] && !fields[end].isNullable())) { break; } } for (int i = 0; i <= end; i++) { written += fields[i].encode(dst, val[i]); } return written; } }
@Override public Class<T> encodedClass() { return wrapped.encodedClass(); }
for (int i = 0; i < this.fields.length; i++) { DataType dt = this.fields[i]; if (!dt.isOrderPreserving()) { preservesOrder = false; if (i < this.fields.length - 2 && !dt.isSkippable()) { throw new IllegalArgumentException("Field in position " + i + " is not skippable. Non-right-most struct fields must be skippable."); if (!dt.isSkippable()) { skippable = false;
PositionedByteRange[] encodedGeneric = new PositionedByteRange[constructorArgs.length]; PositionedByteRange[] encodedSpecialized = new PositionedByteRange[constructorArgs.length]; Constructor<?> ctor = specialized.encodedClass().getConstructor(Object[].class); for (int i = 0; i < vals.length; i++) { vals[i] = ctor.newInstance(new Object[] { constructorArgs[i] }); generic.encodedLength(constructorArgs[i])); encodedSpecialized[i] = new SimplePositionedMutableByteRange( specialized.encodedLength(vals[i])); generic.encode(encodedGeneric[i], constructorArgs[i]); encodedGeneric[i].setPosition(0); specialized.encode(encodedSpecialized[i], vals[i]); encodedSpecialized[i].setPosition(0); assertArrayEquals(encodedGeneric[i].getBytes(), encodedSpecialized[i].getBytes()); assertEquals( "Specialized encoder does not preserve sort order at position " + i, vals[i], specialized.decode(encodedSpecialized[i]));
@Test public void testEncodedLength() { PositionedByteRange buff = new SimplePositionedMutableByteRange(20); for (DataType<String> type : new OrderedString[] { OrderedString.ASCENDING, OrderedString.DESCENDING }) { for (String val : VALUES) { buff.setPosition(0); type.encode(buff, val); assertEquals( "encodedLength does not match actual, " + val, buff.getPosition(), type.encodedLength(val)); } } } }
/** * Bypass the next encoded value. * @return the number of bytes skipped. */ public int skip() { if (!hasNext()) { throw new NoSuchElementException(); } DataType<?> t = types[idx++]; if (src.getPosition() == src.getLength() && t.isNullable()) { return 0; } return t.skip(src); } }
@Test public void testReadWrite() { for (int limit : limits) { PositionedByteRange buff = new SimplePositionedMutableByteRange(limit); for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) { for (byte[] val : VALUES) { buff.setPosition(0); DataType<byte[]> type = new FixedLengthWrapper<>(new RawBytes(ord), limit); assertEquals(limit, type.encode(buff, val)); buff.setPosition(0); byte[] actual = type.decode(buff); assertTrue("Decoding output differs from expected", Bytes.equals(val, 0, val.length, actual, 0, val.length)); buff.setPosition(0); assertEquals(limit, type.skip(buff)); } } } }
@SuppressWarnings("unchecked") @Override public int encodedLength(Object[] val) { assert fields.length >= val.length; int sum = 0; for (int i = 0; i < val.length; i++) { sum += fields[i].encodedLength(val[i]); } return sum; }
@Test public void testSkipSkippable() { PositionedByteRange buff = new SimplePositionedMutableByteRange(14); for (OrderedString t : new OrderedString[] { OrderedString.ASCENDING, OrderedString.DESCENDING }) { for (byte[] term : TERMINATORS) { for (String val : VALUES_STRINGS) { buff.setPosition(0); DataType<String> type = new TerminatedWrapper<>(t, term); int expected = val.length() + 2 + term.length; assertEquals(expected, type.encode(buff, val)); buff.setPosition(0); assertEquals(expected, type.skip(buff)); assertEquals(expected, buff.getPosition()); } } } }
@Test public void testReadWriteSkippable() { PositionedByteRange buff = new SimplePositionedMutableByteRange(14); for (OrderedString t : new OrderedString[] { OrderedString.ASCENDING, OrderedString.DESCENDING }) { for (byte[] term : TERMINATORS) { for (String val : VALUES_STRINGS) { buff.setPosition(0); DataType<String> type = new TerminatedWrapper<>(t, term); assertEquals(val.length() + 2 + term.length, type.encode(buff, val)); buff.setPosition(0); assertEquals(val, type.decode(buff)); assertEquals(val.length() + 2 + term.length, buff.getPosition()); } } } }
@Override public int encode(PositionedByteRange dst, T val) { if (dst.getRemaining() < length) { throw new IllegalArgumentException("Not enough buffer remaining. dst.offset: " + dst.getOffset() + " dst.length: " + dst.getLength() + " dst.position: " + dst.getPosition() + " max length: " + length); } int written = base.encode(dst, val); if (written > length) { throw new IllegalArgumentException("Length of encoded value (" + written + ") exceeds max length (" + length + ")."); } // TODO: is the zero-padding appropriate? for (; written < length; written++) { dst.put((byte) 0x00); } return written; } }
@Override public Order getOrder() { return base.getOrder(); }
@Override public boolean isOrderPreserving() { return wrapped.isOrderPreserving(); }
@Override public boolean isSkippable() { return typeA.isSkippable() && typeB.isSkippable(); }
@Override public T decode(PositionedByteRange src) { if (wrapped.isSkippable()) { T ret = wrapped.decode(src); src.setPosition(src.getPosition() + term.length); return ret; } else { // find the terminator position int term = terminatorPosition(src); if (-1 == term) { throw new IllegalArgumentException("Terminator sequence not found."); } byte[] b = new byte[term - src.getPosition()]; src.get(b); // TODO: should we assert that b.position == b.length? T ret = wrapped.decode(new SimplePositionedMutableByteRange(b)); src.get(this.term); return ret; } }
/** * Skip {@code src}'s position forward over one encoded value. * @param src the buffer containing the encoded value. * @return number of bytes skipped. * @throws IllegalArgumentException when the terminator sequence is not found. */ @Override public int skip(PositionedByteRange src) { if (wrapped.isSkippable()) { int ret = wrapped.skip(src); src.setPosition(src.getPosition() + term.length); return ret + term.length; } else { // find the terminator position final int start = src.getPosition(); int skipped = terminatorPosition(src); if (-1 == skipped) { throw new IllegalArgumentException("Terminator sequence not found."); } skipped += term.length; src.setPosition(skipped); return skipped - start; } }
@Override public boolean isNullable() { return wrapped.isNullable(); }