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_ANYXML_MODELED:
114 streamAnyxmlModeled(writer, nodeHeader);
116 case MagnesiumNode.NODE_LIST_ENTRY:
117 streamListEntry(writer, parent, nodeHeader);
119 case MagnesiumNode.NODE_LEAFSET_ENTRY:
120 streamLeafsetEntry(writer, parent, nodeHeader);
122 case MagnesiumNode.NODE_MAP_ENTRY:
123 streamMapEntry(writer, parent, nodeHeader);
126 throw new InvalidNormalizedNodeStreamException("Unexpected node header " + nodeHeader);
130 private void streamAnyxml(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
131 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
132 LOG.trace("Streaming anyxml node {}", identifier);
134 final DOMSource value = readDOMSource();
135 if (writer.startAnyxmlNode(identifier, DOMSource.class)) {
136 writer.domSourceValue(value);
141 private void streamAnyxmlModeled(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
143 // TODO: decide how to deal with these
144 throw new UnsupportedOperationException("Reading YANG-modeled anyxml was never supported");
147 private void streamAugmentation(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
148 final AugmentationIdentifier augIdentifier = decodeAugmentationIdentifier(nodeHeader);
149 LOG.trace("Streaming augmentation node {}", augIdentifier);
150 writer.startAugmentationNode(augIdentifier);
151 commonStreamContainer(writer, augIdentifier);
154 private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
155 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
156 LOG.trace("Streaming choice node {}", identifier);
157 writer.startChoiceNode(identifier, UNKNOWN_SIZE);
158 commonStreamContainer(writer, identifier);
161 private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
162 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
163 LOG.trace("Streaming container node {}", identifier);
164 writer.startContainerNode(identifier, UNKNOWN_SIZE);
165 commonStreamContainer(writer, identifier);
168 private void streamLeaf(final NormalizedNodeStreamWriter writer, final PathArgument parent, final byte nodeHeader)
170 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
171 LOG.trace("Streaming leaf node {}", identifier);
172 writer.startLeafNode(identifier);
175 if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
176 if (!(parent instanceof NodeIdentifierWithPredicates)) {
177 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
181 value = ((NodeIdentifierWithPredicates) parent).getValue(identifier.getNodeType());
183 throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
184 + " in parent " + parent);
187 value = readLeafValue();
190 writer.scalarValue(value);
194 private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
195 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
196 LOG.trace("Streaming leaf set node {}", identifier);
197 writer.startLeafSet(identifier, UNKNOWN_SIZE);
198 commonStreamContainer(writer, identifier);
201 private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
203 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
204 LOG.trace("Streaming ordered leaf set node {}", identifier);
205 writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
207 commonStreamContainer(writer, identifier);
210 private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
211 final byte nodeHeader) throws IOException {
212 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
213 final Object value = readLeafValue();
214 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
215 LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
216 writer.startLeafSetEntryNode(leafIdentifier);
217 writer.scalarValue(value);
221 private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
222 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
223 writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
224 commonStreamContainer(writer, identifier);
227 private void streamListEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
228 final byte nodeHeader) throws IOException {
229 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
230 LOG.trace("Streaming unkeyed list item node {}", identifier);
231 writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
232 commonStreamContainer(writer, identifier);
235 private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
236 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
237 LOG.trace("Streaming map node {}", identifier);
238 writer.startMapNode(identifier, UNKNOWN_SIZE);
239 commonStreamContainer(writer, identifier);
242 private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
243 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
244 LOG.trace("Streaming ordered map node {}", identifier);
245 writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
246 commonStreamContainer(writer, identifier);
249 private void streamMapEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
250 final byte nodeHeader) throws IOException {
251 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
254 switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
255 case MagnesiumNode.PREDICATE_ZERO:
258 case MagnesiumNode.PREDICATE_ONE:
261 case MagnesiumNode.PREDICATE_1B:
262 size = input.readUnsignedByte();
264 case MagnesiumNode.PREDICATE_4B:
265 size = input.readInt();
268 // ISE on purpose: this should never ever happen
269 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
273 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
274 LOG.trace("Streaming map entry node {}", identifier);
275 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
276 commonStreamContainer(writer, identifier);
279 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
281 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
282 streamNormalizedNode(writer, parent, nodeType);
287 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
288 final QNameModule module = decodeQNameModule();
289 final String localName = readRefString();
290 final NodeIdentifier nodeId;
292 nodeId = QNameFactory.getNodeIdentifier(module, localName);
293 } catch (ExecutionException e) {
294 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
298 codedNodeIdentifiers.add(nodeId);
302 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
303 return decodeNodeIdentifier(nodeHeader, null);
306 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
308 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
309 case MagnesiumNode.ADDR_DEFINE:
310 return readNodeIdentifier();
311 case MagnesiumNode.ADDR_LOOKUP_1B:
312 index = input.readUnsignedByte();
314 case MagnesiumNode.ADDR_LOOKUP_4B:
315 index = input.readInt();
317 case MagnesiumNode.ADDR_PARENT:
318 if (parent instanceof NodeIdentifier) {
319 return (NodeIdentifier) parent;
321 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
323 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
328 return codedNodeIdentifiers.get(index);
329 } catch (IndexOutOfBoundsException e) {
330 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
334 private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
336 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
337 case MagnesiumNode.ADDR_DEFINE:
338 return readAugmentationIdentifier();
339 case MagnesiumNode.ADDR_LOOKUP_1B:
340 index = input.readUnsignedByte();
342 case MagnesiumNode.ADDR_LOOKUP_4B:
343 index = input.readInt();
346 throw new InvalidNormalizedNodeStreamException(
347 "Unexpected augmentation identifier addressing in header " + nodeHeader);
351 return codedAugments.get(index);
352 } catch (IndexOutOfBoundsException e) {
353 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
358 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
359 final byte type = input.readByte();
360 if (type == MagnesiumValue.YIID) {
361 return readYangInstanceIdentifier(input.readInt());
362 } else if (type >= MagnesiumValue.YIID_0) {
363 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
364 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
366 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
370 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
372 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
373 for (int i = 0; i < size; ++i) {
374 builder.add(readPathArgument());
376 return YangInstanceIdentifier.create(builder.build());
377 } else if (size == 0) {
378 return YangInstanceIdentifier.empty();
380 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
385 public final QName readQName() throws IOException {
386 final byte type = input.readByte();
388 case MagnesiumValue.QNAME:
389 return decodeQName();
390 case MagnesiumValue.QNAME_REF_1B:
391 return decodeQNameRef1();
392 case MagnesiumValue.QNAME_REF_2B:
393 return decodeQNameRef2();
394 case MagnesiumValue.QNAME_REF_4B:
395 return decodeQNameRef4();
397 throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
402 public final PathArgument readPathArgument() throws IOException {
403 final byte header = input.readByte();
404 switch (header & MagnesiumPathArgument.TYPE_MASK) {
405 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER:
406 return readAugmentationIdentifier(header);
407 case MagnesiumPathArgument.NODE_IDENTIFIER:
408 verifyPathIdentifierOnly(header);
409 return readNodeIdentifier(header);
410 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
411 return readNodeIdentifierWithPredicates(header);
412 case MagnesiumPathArgument.NODE_WITH_VALUE:
413 verifyPathIdentifierOnly(header);
414 return readNodeWithValue(header);
415 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER:
416 verifyPathIdentifierOnly(header);
417 return MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
419 throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
423 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
424 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
425 codedAugments.add(result);
429 private AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
430 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
432 case MagnesiumPathArgument.AID_COUNT_1B:
433 return readAugmentationIdentifier(input.readUnsignedByte());
434 case MagnesiumPathArgument.AID_COUNT_2B:
435 return readAugmentationIdentifier(input.readUnsignedShort());
436 case MagnesiumPathArgument.AID_COUNT_4B:
437 return readAugmentationIdentifier(input.readInt());
439 return readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
443 private AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
445 final List<QName> qnames = new ArrayList<>(size);
446 for (int i = 0; i < size; ++i) {
447 qnames.add(readQName());
449 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
450 } else if (size == 0) {
453 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
457 private NodeIdentifier readNodeIdentifier() throws IOException {
458 return decodeNodeIdentifier();
461 private NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
462 switch (header & MagnesiumPathArgument.QNAME_MASK) {
463 case MagnesiumPathArgument.QNAME_DEF:
464 return decodeNodeIdentifier();
465 case MagnesiumPathArgument.QNAME_REF_1B:
466 return decodeNodeIdentifierRef1();
467 case MagnesiumPathArgument.QNAME_REF_2B:
468 return decodeNodeIdentifierRef2();
469 case MagnesiumPathArgument.QNAME_REF_4B:
470 return decodeNodeIdentifierRef4();
472 throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
476 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header) throws IOException {
477 final QName qname = readNodeIdentifier(header).getNodeType();
478 switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
479 case MagnesiumPathArgument.SIZE_1B:
480 return readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
481 case MagnesiumPathArgument.SIZE_2B:
482 return readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
483 case MagnesiumPathArgument.SIZE_4B:
484 return readNodeIdentifierWithPredicates(qname, input.readInt());
486 return readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
490 private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
493 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
494 } else if (size > 1) {
495 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
496 for (int i = 0; i < size; ++i) {
497 builder.put(readQName(), readLeafValue());
499 return NodeIdentifierWithPredicates.of(qname, builder.build());
500 } else if (size == 0) {
501 return NodeIdentifierWithPredicates.of(qname);
503 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
507 private NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
508 final QName qname = readNodeIdentifier(header).getNodeType();
509 return new NodeWithValue<>(qname, readLeafValue());
512 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
513 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
514 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
518 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
519 return lookupNodeIdentifier(input.readUnsignedByte());
522 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
523 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
526 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
527 return lookupNodeIdentifier(input.readInt());
530 private @NonNull QName decodeQName() throws IOException {
531 return decodeNodeIdentifier().getNodeType();
534 private @NonNull QName decodeQNameRef1() throws IOException {
535 return lookupQName(input.readUnsignedByte());
538 private @NonNull QName decodeQNameRef2() throws IOException {
539 return lookupQName(input.readUnsignedShort() + 256);
542 private @NonNull QName decodeQNameRef4() throws IOException {
543 return lookupQName(input.readInt());
546 private @NonNull QNameModule decodeQNameModule() throws IOException {
547 final byte type = input.readByte();
550 case MagnesiumValue.MODREF_1B:
551 index = input.readUnsignedByte();
553 case MagnesiumValue.MODREF_2B:
554 index = input.readUnsignedShort() + 256;
556 case MagnesiumValue.MODREF_4B:
557 index = input.readInt();
560 return decodeQNameModuleDef(type);
564 return codedModules.get(index);
565 } catch (IndexOutOfBoundsException e) {
566 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
570 // QNameModule definition, i.e. two encoded strings
571 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
572 final String namespace = readRefString(type);
574 final byte refType = input.readByte();
575 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
576 final QNameModule module;
578 module = QNameFactory.createModule(namespace, revision);
579 } catch (UncheckedExecutionException e) {
580 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
584 codedModules.add(module);
588 private @NonNull String readRefString() throws IOException {
589 return readRefString(input.readByte());
592 private @NonNull String readRefString(final byte type) throws IOException {
595 case MagnesiumValue.STRING_REF_1B:
596 return lookupString(input.readUnsignedByte());
597 case MagnesiumValue.STRING_REF_2B:
598 return lookupString(input.readUnsignedShort() + 256);
599 case MagnesiumValue.STRING_REF_4B:
600 return lookupString(input.readInt());
601 case MagnesiumValue.STRING_EMPTY:
603 case MagnesiumValue.STRING_2B:
606 case MagnesiumValue.STRING_4B:
609 case MagnesiumValue.STRING_CHARS:
610 str = readCharsString();
612 case MagnesiumValue.STRING_UTF:
613 str = input.readUTF();
616 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
619 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
620 codedStrings.add(verifyNotNull(str));
624 private @NonNull String readString() throws IOException {
625 final byte type = input.readByte();
627 case MagnesiumValue.STRING_EMPTY:
629 case MagnesiumValue.STRING_UTF:
630 return input.readUTF();
631 case MagnesiumValue.STRING_2B:
632 return readString2();
633 case MagnesiumValue.STRING_4B:
634 return readString4();
635 case MagnesiumValue.STRING_CHARS:
636 return readCharsString();
638 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
642 private @NonNull String readString2() throws IOException {
643 return readByteString(input.readUnsignedShort());
646 private @NonNull String readString4() throws IOException {
647 return readByteString(input.readInt());
650 private @NonNull String readByteString(final int size) throws IOException {
652 final byte[] bytes = new byte[size];
653 input.readFully(bytes);
654 return new String(bytes, StandardCharsets.UTF_8);
655 } else if (size == 0) {
658 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
662 private @NonNull String readCharsString() throws IOException {
663 final int size = input.readInt();
665 final char[] chars = new char[size];
666 for (int i = 0; i < size; ++i) {
667 chars[i] = input.readChar();
669 return String.valueOf(chars);
670 } else if (size == 0) {
673 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
677 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
679 return codedNodeIdentifiers.get(index);
680 } catch (IndexOutOfBoundsException e) {
681 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
685 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
686 return lookupNodeIdentifier(index).getNodeType();
689 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
691 return codedStrings.get(index);
692 } catch (IndexOutOfBoundsException e) {
693 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
697 private @NonNull DOMSource readDOMSource() throws IOException {
698 final String str = readString();
700 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
701 .getDocumentElement());
702 } catch (SAXException e) {
703 throw new IOException("Error parsing XML: " + str, e);
707 private @NonNull Object readLeafValue() throws IOException {
708 final byte type = input.readByte();
710 case MagnesiumValue.BOOLEAN_FALSE:
711 return Boolean.FALSE;
712 case MagnesiumValue.BOOLEAN_TRUE:
714 case MagnesiumValue.EMPTY:
715 return Empty.getInstance();
716 case MagnesiumValue.INT8:
717 return input.readByte();
718 case MagnesiumValue.INT8_0:
720 case MagnesiumValue.INT16:
721 return input.readShort();
722 case MagnesiumValue.INT16_0:
724 case MagnesiumValue.INT32:
725 return input.readInt();
726 case MagnesiumValue.INT32_0:
728 case MagnesiumValue.INT32_2B:
729 return input.readShort() & 0xFFFF;
730 case MagnesiumValue.INT64:
731 return input.readLong();
732 case MagnesiumValue.INT64_0:
734 case MagnesiumValue.INT64_4B:
735 return input.readInt() & 0xFFFFFFFFL;
736 case MagnesiumValue.UINT8:
737 return Uint8.fromByteBits(input.readByte());
738 case MagnesiumValue.UINT8_0:
740 case MagnesiumValue.UINT16:
741 return Uint16.fromShortBits(input.readShort());
742 case MagnesiumValue.UINT16_0:
744 case MagnesiumValue.UINT32:
745 return Uint32.fromIntBits(input.readInt());
746 case MagnesiumValue.UINT32_0:
748 case MagnesiumValue.UINT32_2B:
749 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
750 case MagnesiumValue.UINT64:
751 return Uint64.fromLongBits(input.readLong());
752 case MagnesiumValue.UINT64_0:
754 case MagnesiumValue.UINT64_4B:
755 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
756 case MagnesiumValue.BIGDECIMAL:
757 // FIXME: use string -> BigDecimal cache
758 return new BigDecimal(input.readUTF());
759 case MagnesiumValue.BIGINTEGER:
760 return readBigInteger();
761 case MagnesiumValue.STRING_EMPTY:
763 case MagnesiumValue.STRING_UTF:
764 return input.readUTF();
765 case MagnesiumValue.STRING_2B:
766 return readString2();
767 case MagnesiumValue.STRING_4B:
768 return readString4();
769 case MagnesiumValue.STRING_CHARS:
770 return readCharsString();
771 case MagnesiumValue.BINARY_0:
773 case MagnesiumValue.BINARY_1B:
774 return readBinary(128 + input.readUnsignedByte());
775 case MagnesiumValue.BINARY_2B:
776 return readBinary(384 + input.readUnsignedShort());
777 case MagnesiumValue.BINARY_4B:
778 return readBinary(input.readInt());
779 case MagnesiumValue.YIID_0:
780 return YangInstanceIdentifier.empty();
781 case MagnesiumValue.YIID:
782 return readYangInstanceIdentifier(input.readInt());
783 case MagnesiumValue.QNAME:
784 return decodeQName();
785 case MagnesiumValue.QNAME_REF_1B:
786 return decodeQNameRef1();
787 case MagnesiumValue.QNAME_REF_2B:
788 return decodeQNameRef2();
789 case MagnesiumValue.QNAME_REF_4B:
790 return decodeQNameRef4();
791 case MagnesiumValue.BITS_0:
792 return ImmutableSet.of();
793 case MagnesiumValue.BITS_1B:
794 return readBits(input.readUnsignedByte() + 29);
795 case MagnesiumValue.BITS_2B:
796 return readBits(input.readUnsignedShort() + 285);
797 case MagnesiumValue.BITS_4B:
798 return readBits(input.readInt());
801 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
802 return readBinary(type - MagnesiumValue.BINARY_0);
803 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
804 return readBits(type - MagnesiumValue.BITS_0);
805 } else if (type > MagnesiumValue.YIID_0) {
806 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
807 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
809 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
814 abstract @NonNull BigInteger readBigInteger() throws IOException;
816 private byte @NonNull [] readBinary(final int size) throws IOException {
818 final byte[] ret = new byte[size];
819 input.readFully(ret);
821 } else if (size == 0) {
824 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
828 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
830 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
831 for (int i = 0; i < size; ++i) {
832 builder.add(readRefString());
834 return builder.build();
835 } else if (size == 0) {
836 return ImmutableSet.of();
838 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
842 private static byte mask(final byte header, final byte mask) {
843 return (byte) (header & mask);
846 private static int rshift(final byte header, final byte shift) {
847 return (header & 0xFF) >>> shift;