2 * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.codec.binfmt;
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableList.Builder;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.util.concurrent.UncheckedExecutionException;
19 import java.io.DataInput;
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.math.BigDecimal;
23 import java.math.BigInteger;
24 import java.nio.charset.StandardCharsets;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.ExecutionException;
28 import javax.xml.transform.dom.DOMSource;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
31 import org.opendaylight.yangtools.util.xml.UntrustedXML;
32 import org.opendaylight.yangtools.yang.common.Empty;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.common.QNameModule;
35 import org.opendaylight.yangtools.yang.common.Uint16;
36 import org.opendaylight.yangtools.yang.common.Uint32;
37 import org.opendaylight.yangtools.yang.common.Uint64;
38 import org.opendaylight.yangtools.yang.common.Uint8;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
45 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.xml.sax.InputSource;
49 import org.xml.sax.SAXException;
52 * Abstract base class for NormalizedNodeDataInput based on {@link MagnesiumNode}, {@link MagnesiumPathArgument} and
53 * {@link MagnesiumValue}.
55 abstract class AbstractMagnesiumDataInput extends AbstractNormalizedNodeDataInput {
56 private static final Logger LOG = LoggerFactory.getLogger(AbstractMagnesiumDataInput.class);
58 // Known singleton objects
59 private static final @NonNull Byte INT8_0 = 0;
60 private static final @NonNull Short INT16_0 = 0;
61 private static final @NonNull Integer INT32_0 = 0;
62 private static final @NonNull Long INT64_0 = 0L;
63 private static final byte @NonNull[] BINARY_0 = new byte[0];
64 private static final @NonNull AugmentationIdentifier EMPTY_AID = AugmentationIdentifier.create(ImmutableSet.of());
66 private final List<AugmentationIdentifier> codedAugments = new ArrayList<>();
67 private final List<NodeIdentifier> codedNodeIdentifiers = new ArrayList<>();
68 private final List<QNameModule> codedModules = new ArrayList<>();
69 private final List<String> codedStrings = new ArrayList<>();
71 AbstractMagnesiumDataInput(final DataInput input) {
76 public final void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
77 streamNormalizedNode(requireNonNull(writer), null, input.readByte());
80 private void streamNormalizedNode(final NormalizedNodeStreamWriter writer, final PathArgument parent,
81 final byte nodeHeader) throws IOException {
82 switch (nodeHeader & MagnesiumNode.TYPE_MASK) {
83 case MagnesiumNode.NODE_LEAF:
84 streamLeaf(writer, parent, nodeHeader);
86 case MagnesiumNode.NODE_CONTAINER:
87 streamContainer(writer, nodeHeader);
89 case MagnesiumNode.NODE_LIST:
90 streamList(writer, nodeHeader);
92 case MagnesiumNode.NODE_MAP:
93 streamMap(writer, nodeHeader);
95 case MagnesiumNode.NODE_MAP_ORDERED:
96 streamMapOrdered(writer, nodeHeader);
98 case MagnesiumNode.NODE_LEAFSET:
99 streamLeafset(writer, nodeHeader);
101 case MagnesiumNode.NODE_LEAFSET_ORDERED:
102 streamLeafsetOrdered(writer, nodeHeader);
104 case MagnesiumNode.NODE_CHOICE:
105 streamChoice(writer, nodeHeader);
107 case MagnesiumNode.NODE_AUGMENTATION:
108 streamAugmentation(writer, nodeHeader);
110 case MagnesiumNode.NODE_ANYXML:
111 streamAnyxml(writer, nodeHeader);
113 case MagnesiumNode.NODE_LIST_ENTRY:
114 streamListEntry(writer, parent, nodeHeader);
116 case MagnesiumNode.NODE_LEAFSET_ENTRY:
117 streamLeafsetEntry(writer, parent, nodeHeader);
119 case MagnesiumNode.NODE_MAP_ENTRY:
120 streamMapEntry(writer, parent, nodeHeader);
123 throw new InvalidNormalizedNodeStreamException("Unexpected node header " + nodeHeader);
127 private void streamAnyxml(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
128 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
129 LOG.trace("Streaming anyxml node {}", identifier);
131 final DOMSource value = readDOMSource();
132 if (writer.startAnyxmlNode(identifier, DOMSource.class)) {
133 writer.domSourceValue(value);
138 private void streamAugmentation(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
139 final AugmentationIdentifier augIdentifier = decodeAugmentationIdentifier(nodeHeader);
140 LOG.trace("Streaming augmentation node {}", augIdentifier);
141 writer.startAugmentationNode(augIdentifier);
142 commonStreamContainer(writer, augIdentifier);
145 private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
146 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
147 LOG.trace("Streaming choice node {}", identifier);
148 writer.startChoiceNode(identifier, UNKNOWN_SIZE);
149 commonStreamContainer(writer, identifier);
152 private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
153 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
154 LOG.trace("Streaming container node {}", identifier);
155 writer.startContainerNode(identifier, UNKNOWN_SIZE);
156 commonStreamContainer(writer, identifier);
159 private void streamLeaf(final NormalizedNodeStreamWriter writer, final PathArgument parent, final byte nodeHeader)
161 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
162 LOG.trace("Streaming leaf node {}", identifier);
163 writer.startLeafNode(identifier);
166 if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
167 if (!(parent instanceof NodeIdentifierWithPredicates)) {
168 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
172 value = ((NodeIdentifierWithPredicates) parent).getValue(identifier.getNodeType());
174 throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
175 + " in parent " + parent);
178 value = readLeafValue();
181 writer.scalarValue(value);
185 private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
186 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
187 LOG.trace("Streaming leaf set node {}", identifier);
188 writer.startLeafSet(identifier, UNKNOWN_SIZE);
189 commonStreamContainer(writer, identifier);
192 private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
194 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
195 LOG.trace("Streaming ordered leaf set node {}", identifier);
196 writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
198 commonStreamContainer(writer, identifier);
201 private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
202 final byte nodeHeader) throws IOException {
203 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
204 final Object value = readLeafValue();
205 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
206 LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
207 writer.startLeafSetEntryNode(leafIdentifier);
208 writer.scalarValue(value);
212 private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
213 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
214 writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
215 commonStreamContainer(writer, identifier);
218 private void streamListEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
219 final byte nodeHeader) throws IOException {
220 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
221 LOG.trace("Streaming unkeyed list item node {}", identifier);
222 writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
223 commonStreamContainer(writer, identifier);
226 private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
227 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
228 LOG.trace("Streaming map node {}", identifier);
229 writer.startMapNode(identifier, UNKNOWN_SIZE);
230 commonStreamContainer(writer, identifier);
233 private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
234 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
235 LOG.trace("Streaming ordered map node {}", identifier);
236 writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
237 commonStreamContainer(writer, identifier);
240 private void streamMapEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
241 final byte nodeHeader) throws IOException {
242 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
245 switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
246 case MagnesiumNode.PREDICATE_ZERO:
249 case MagnesiumNode.PREDICATE_ONE:
252 case MagnesiumNode.PREDICATE_1B:
253 size = input.readUnsignedByte();
255 case MagnesiumNode.PREDICATE_4B:
256 size = input.readInt();
259 // ISE on purpose: this should never ever happen
260 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
264 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
265 LOG.trace("Streaming map entry node {}", identifier);
266 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
267 commonStreamContainer(writer, identifier);
270 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
272 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
273 streamNormalizedNode(writer, parent, nodeType);
278 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
279 final QNameModule module = decodeQNameModule();
280 final String localName = readRefString();
281 final NodeIdentifier nodeId;
283 nodeId = QNameFactory.getNodeIdentifier(module, localName);
284 } catch (ExecutionException e) {
285 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
289 codedNodeIdentifiers.add(nodeId);
293 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
294 return decodeNodeIdentifier(nodeHeader, null);
297 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
299 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
300 case MagnesiumNode.ADDR_DEFINE:
301 return readNodeIdentifier();
302 case MagnesiumNode.ADDR_LOOKUP_1B:
303 index = input.readUnsignedByte();
305 case MagnesiumNode.ADDR_LOOKUP_4B:
306 index = input.readInt();
308 case MagnesiumNode.ADDR_PARENT:
309 if (parent instanceof NodeIdentifier) {
310 return (NodeIdentifier) parent;
312 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
314 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
319 return codedNodeIdentifiers.get(index);
320 } catch (IndexOutOfBoundsException e) {
321 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
325 private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
327 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
328 case MagnesiumNode.ADDR_DEFINE:
329 return readAugmentationIdentifier();
330 case MagnesiumNode.ADDR_LOOKUP_1B:
331 index = input.readUnsignedByte();
333 case MagnesiumNode.ADDR_LOOKUP_4B:
334 index = input.readInt();
337 throw new InvalidNormalizedNodeStreamException(
338 "Unexpected augmentation identifier addressing in header " + nodeHeader);
342 return codedAugments.get(index);
343 } catch (IndexOutOfBoundsException e) {
344 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
349 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
350 final byte type = input.readByte();
351 if (type == MagnesiumValue.YIID) {
352 return readYangInstanceIdentifier(input.readInt());
353 } else if (type >= MagnesiumValue.YIID_0) {
354 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
355 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
357 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
361 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
363 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
364 for (int i = 0; i < size; ++i) {
365 builder.add(readPathArgument());
367 return YangInstanceIdentifier.create(builder.build());
368 } else if (size == 0) {
369 return YangInstanceIdentifier.empty();
371 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
376 public final QName readQName() throws IOException {
377 final byte type = input.readByte();
379 case MagnesiumValue.QNAME:
380 return decodeQName();
381 case MagnesiumValue.QNAME_REF_1B:
382 return decodeQNameRef1();
383 case MagnesiumValue.QNAME_REF_2B:
384 return decodeQNameRef2();
385 case MagnesiumValue.QNAME_REF_4B:
386 return decodeQNameRef4();
388 throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
393 public final PathArgument readPathArgument() throws IOException {
394 final byte header = input.readByte();
395 switch (header & MagnesiumPathArgument.TYPE_MASK) {
396 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER:
397 return readAugmentationIdentifier(header);
398 case MagnesiumPathArgument.NODE_IDENTIFIER:
399 verifyPathIdentifierOnly(header);
400 return readNodeIdentifier(header);
401 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
402 return readNodeIdentifierWithPredicates(header);
403 case MagnesiumPathArgument.NODE_WITH_VALUE:
404 verifyPathIdentifierOnly(header);
405 return readNodeWithValue(header);
406 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER:
407 verifyPathIdentifierOnly(header);
408 return MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
410 throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
414 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
415 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
416 codedAugments.add(result);
420 private AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
421 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
423 case MagnesiumPathArgument.AID_COUNT_1B:
424 return readAugmentationIdentifier(input.readUnsignedByte());
425 case MagnesiumPathArgument.AID_COUNT_2B:
426 return readAugmentationIdentifier(input.readUnsignedShort());
427 case MagnesiumPathArgument.AID_COUNT_4B:
428 return readAugmentationIdentifier(input.readInt());
430 return readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
434 private AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
436 final List<QName> qnames = new ArrayList<>(size);
437 for (int i = 0; i < size; ++i) {
438 qnames.add(readQName());
440 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
441 } else if (size == 0) {
444 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
448 private NodeIdentifier readNodeIdentifier() throws IOException {
449 return decodeNodeIdentifier();
452 private NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
453 switch (header & MagnesiumPathArgument.QNAME_MASK) {
454 case MagnesiumPathArgument.QNAME_DEF:
455 return decodeNodeIdentifier();
456 case MagnesiumPathArgument.QNAME_REF_1B:
457 return decodeNodeIdentifierRef1();
458 case MagnesiumPathArgument.QNAME_REF_2B:
459 return decodeNodeIdentifierRef2();
460 case MagnesiumPathArgument.QNAME_REF_4B:
461 return decodeNodeIdentifierRef4();
463 throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
467 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header) throws IOException {
468 final QName qname = readNodeIdentifier(header).getNodeType();
469 switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
470 case MagnesiumPathArgument.SIZE_1B:
471 return readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
472 case MagnesiumPathArgument.SIZE_2B:
473 return readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
474 case MagnesiumPathArgument.SIZE_4B:
475 return readNodeIdentifierWithPredicates(qname, input.readInt());
477 return readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
481 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
484 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
485 } else if (size > 1) {
486 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
487 for (int i = 0; i < size; ++i) {
488 builder.put(readQName(), readLeafValue());
490 return NodeIdentifierWithPredicates.of(qname, builder.build());
491 } else if (size == 0) {
492 return NodeIdentifierWithPredicates.of(qname);
494 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
498 private NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
499 final QName qname = readNodeIdentifier(header).getNodeType();
500 return new NodeWithValue<>(qname, readLeafValue());
503 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
504 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
505 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
509 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
510 return lookupNodeIdentifier(input.readUnsignedByte());
513 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
514 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
517 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
518 return lookupNodeIdentifier(input.readInt());
521 private @NonNull QName decodeQName() throws IOException {
522 return decodeNodeIdentifier().getNodeType();
525 private @NonNull QName decodeQNameRef1() throws IOException {
526 return lookupQName(input.readUnsignedByte());
529 private @NonNull QName decodeQNameRef2() throws IOException {
530 return lookupQName(input.readUnsignedShort() + 256);
533 private @NonNull QName decodeQNameRef4() throws IOException {
534 return lookupQName(input.readInt());
537 private @NonNull QNameModule decodeQNameModule() throws IOException {
538 final byte type = input.readByte();
541 case MagnesiumValue.MODREF_1B:
542 index = input.readUnsignedByte();
544 case MagnesiumValue.MODREF_2B:
545 index = input.readUnsignedShort() + 256;
547 case MagnesiumValue.MODREF_4B:
548 index = input.readInt();
551 return decodeQNameModuleDef(type);
555 return codedModules.get(index);
556 } catch (IndexOutOfBoundsException e) {
557 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
561 // QNameModule definition, i.e. two encoded strings
562 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
563 final String namespace = readRefString(type);
565 final byte refType = input.readByte();
566 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
567 final QNameModule module;
569 module = QNameFactory.createModule(namespace, revision);
570 } catch (UncheckedExecutionException e) {
571 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
575 codedModules.add(module);
579 private @NonNull String readRefString() throws IOException {
580 return readRefString(input.readByte());
583 private @NonNull String readRefString(final byte type) throws IOException {
586 case MagnesiumValue.STRING_REF_1B:
587 return lookupString(input.readUnsignedByte());
588 case MagnesiumValue.STRING_REF_2B:
589 return lookupString(input.readUnsignedShort() + 256);
590 case MagnesiumValue.STRING_REF_4B:
591 return lookupString(input.readInt());
592 case MagnesiumValue.STRING_EMPTY:
594 case MagnesiumValue.STRING_2B:
597 case MagnesiumValue.STRING_4B:
600 case MagnesiumValue.STRING_CHARS:
601 str = readCharsString();
603 case MagnesiumValue.STRING_UTF:
604 str = input.readUTF();
607 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
610 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
611 codedStrings.add(verifyNotNull(str));
615 private @NonNull String readString() throws IOException {
616 final byte type = input.readByte();
618 case MagnesiumValue.STRING_EMPTY:
620 case MagnesiumValue.STRING_UTF:
621 return input.readUTF();
622 case MagnesiumValue.STRING_2B:
623 return readString2();
624 case MagnesiumValue.STRING_4B:
625 return readString4();
626 case MagnesiumValue.STRING_CHARS:
627 return readCharsString();
629 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
633 private @NonNull String readString2() throws IOException {
634 return readByteString(input.readUnsignedShort());
637 private @NonNull String readString4() throws IOException {
638 return readByteString(input.readInt());
641 private @NonNull String readByteString(final int size) throws IOException {
643 final byte[] bytes = new byte[size];
644 input.readFully(bytes);
645 return new String(bytes, StandardCharsets.UTF_8);
646 } else if (size == 0) {
649 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
653 private @NonNull String readCharsString() throws IOException {
654 final int size = input.readInt();
656 final char[] chars = new char[size];
657 for (int i = 0; i < size; ++i) {
658 chars[i] = input.readChar();
660 return String.valueOf(chars);
661 } else if (size == 0) {
664 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
668 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
670 return codedNodeIdentifiers.get(index);
671 } catch (IndexOutOfBoundsException e) {
672 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
676 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
677 return lookupNodeIdentifier(index).getNodeType();
680 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
682 return codedStrings.get(index);
683 } catch (IndexOutOfBoundsException e) {
684 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
688 private @NonNull DOMSource readDOMSource() throws IOException {
689 final String str = readString();
691 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
692 .getDocumentElement());
693 } catch (SAXException e) {
694 throw new IOException("Error parsing XML: " + str, e);
698 private @NonNull Object readLeafValue() throws IOException {
699 final byte type = input.readByte();
701 case MagnesiumValue.BOOLEAN_FALSE:
702 return Boolean.FALSE;
703 case MagnesiumValue.BOOLEAN_TRUE:
705 case MagnesiumValue.EMPTY:
706 return Empty.getInstance();
707 case MagnesiumValue.INT8:
708 return input.readByte();
709 case MagnesiumValue.INT8_0:
711 case MagnesiumValue.INT16:
712 return input.readShort();
713 case MagnesiumValue.INT16_0:
715 case MagnesiumValue.INT32:
716 return input.readInt();
717 case MagnesiumValue.INT32_0:
719 case MagnesiumValue.INT32_2B:
720 return input.readShort() & 0xFFFF;
721 case MagnesiumValue.INT64:
722 return input.readLong();
723 case MagnesiumValue.INT64_0:
725 case MagnesiumValue.INT64_4B:
726 return input.readInt() & 0xFFFFFFFFL;
727 case MagnesiumValue.UINT8:
728 return Uint8.fromByteBits(input.readByte());
729 case MagnesiumValue.UINT8_0:
731 case MagnesiumValue.UINT16:
732 return Uint16.fromShortBits(input.readShort());
733 case MagnesiumValue.UINT16_0:
735 case MagnesiumValue.UINT32:
736 return Uint32.fromIntBits(input.readInt());
737 case MagnesiumValue.UINT32_0:
739 case MagnesiumValue.UINT32_2B:
740 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
741 case MagnesiumValue.UINT64:
742 return Uint64.fromLongBits(input.readLong());
743 case MagnesiumValue.UINT64_0:
745 case MagnesiumValue.UINT64_4B:
746 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
747 case MagnesiumValue.BIGDECIMAL:
748 // FIXME: use string -> BigDecimal cache
749 return new BigDecimal(input.readUTF());
750 case MagnesiumValue.BIGINTEGER:
751 return readBigInteger();
752 case MagnesiumValue.STRING_EMPTY:
754 case MagnesiumValue.STRING_UTF:
755 return input.readUTF();
756 case MagnesiumValue.STRING_2B:
757 return readString2();
758 case MagnesiumValue.STRING_4B:
759 return readString4();
760 case MagnesiumValue.STRING_CHARS:
761 return readCharsString();
762 case MagnesiumValue.BINARY_0:
764 case MagnesiumValue.BINARY_1B:
765 return readBinary(128 + input.readUnsignedByte());
766 case MagnesiumValue.BINARY_2B:
767 return readBinary(384 + input.readUnsignedShort());
768 case MagnesiumValue.BINARY_4B:
769 return readBinary(input.readInt());
770 case MagnesiumValue.YIID_0:
771 return YangInstanceIdentifier.empty();
772 case MagnesiumValue.YIID:
773 return readYangInstanceIdentifier(input.readInt());
774 case MagnesiumValue.QNAME:
775 return decodeQName();
776 case MagnesiumValue.QNAME_REF_1B:
777 return decodeQNameRef1();
778 case MagnesiumValue.QNAME_REF_2B:
779 return decodeQNameRef2();
780 case MagnesiumValue.QNAME_REF_4B:
781 return decodeQNameRef4();
782 case MagnesiumValue.BITS_0:
783 return ImmutableSet.of();
784 case MagnesiumValue.BITS_1B:
785 return readBits(input.readUnsignedByte() + 29);
786 case MagnesiumValue.BITS_2B:
787 return readBits(input.readUnsignedShort() + 285);
788 case MagnesiumValue.BITS_4B:
789 return readBits(input.readInt());
792 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
793 return readBinary(type - MagnesiumValue.BINARY_0);
794 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
795 return readBits(type - MagnesiumValue.BITS_0);
796 } else if (type > MagnesiumValue.YIID_0) {
797 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
798 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
800 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
805 abstract @NonNull BigInteger readBigInteger() throws IOException;
807 private byte @NonNull [] readBinary(final int size) throws IOException {
809 final byte[] ret = new byte[size];
810 input.readFully(ret);
812 } else if (size == 0) {
815 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
819 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
821 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
822 for (int i = 0; i < size; ++i) {
823 builder.add(readRefString());
825 return builder.build();
826 } else if (size == 0) {
827 return ImmutableSet.of();
829 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
833 private static byte mask(final byte header, final byte mask) {
834 return (byte) (header & mask);
837 private static int rshift(final byte header, final byte shift) {
838 return (header & 0xFF) >>> shift;