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.BigInteger;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.concurrent.ExecutionException;
27 import javax.xml.transform.dom.DOMSource;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.opendaylight.yangtools.concepts.Either;
30 import org.opendaylight.yangtools.util.xml.UntrustedXML;
31 import org.opendaylight.yangtools.yang.common.Decimal64;
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 nip)) {
168 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
172 value = nip.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);
244 final int size = switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
245 case MagnesiumNode.PREDICATE_ZERO -> 0;
246 case MagnesiumNode.PREDICATE_ONE -> 1;
247 case MagnesiumNode.PREDICATE_1B -> input.readUnsignedByte();
248 case MagnesiumNode.PREDICATE_4B -> input.readInt();
250 // ISE on purpose: this should never ever happen
251 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
254 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
255 LOG.trace("Streaming map entry node {}", identifier);
256 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
257 commonStreamContainer(writer, identifier);
260 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
262 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
263 streamNormalizedNode(writer, parent, nodeType);
268 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
269 final QNameModule module = decodeQNameModule();
270 final String localName = readRefString();
271 final NodeIdentifier nodeId;
273 nodeId = QNameFactory.getNodeIdentifier(module, localName);
274 } catch (ExecutionException e) {
275 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
279 codedNodeIdentifiers.add(nodeId);
283 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
284 return decodeNodeIdentifier(nodeHeader, null);
287 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
289 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
290 case MagnesiumNode.ADDR_DEFINE:
291 return readNodeIdentifier();
292 case MagnesiumNode.ADDR_LOOKUP_1B:
293 index = input.readUnsignedByte();
295 case MagnesiumNode.ADDR_LOOKUP_4B:
296 index = input.readInt();
298 case MagnesiumNode.ADDR_PARENT:
299 if (parent instanceof NodeIdentifier nid) {
302 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
304 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
309 return codedNodeIdentifiers.get(index);
310 } catch (IndexOutOfBoundsException e) {
311 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
315 private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
317 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
318 case MagnesiumNode.ADDR_DEFINE:
319 return readAugmentationIdentifier();
320 case MagnesiumNode.ADDR_LOOKUP_1B:
321 index = input.readUnsignedByte();
323 case MagnesiumNode.ADDR_LOOKUP_4B:
324 index = input.readInt();
327 throw new InvalidNormalizedNodeStreamException(
328 "Unexpected augmentation identifier addressing in header " + nodeHeader);
332 return codedAugments.get(index);
333 } catch (IndexOutOfBoundsException e) {
334 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
339 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
340 final byte type = input.readByte();
341 if (type == MagnesiumValue.YIID) {
342 return readYangInstanceIdentifier(input.readInt());
343 } else if (type >= MagnesiumValue.YIID_0) {
344 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
345 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
347 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
351 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
353 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
354 for (int i = 0; i < size; ++i) {
355 builder.add(readPathArgument());
357 return YangInstanceIdentifier.create(builder.build());
358 } else if (size == 0) {
359 return YangInstanceIdentifier.empty();
361 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
366 public final QName readQName() throws IOException {
367 final byte type = input.readByte();
368 return switch (type) {
369 case MagnesiumValue.QNAME -> decodeQName();
370 case MagnesiumValue.QNAME_REF_1B -> decodeQNameRef1();
371 case MagnesiumValue.QNAME_REF_2B -> decodeQNameRef2();
372 case MagnesiumValue.QNAME_REF_4B -> decodeQNameRef4();
373 default -> throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
378 @Deprecated(since = "11.0.0", forRemoval = true)
379 public final Either<PathArgument, LegacyPathArgument> readLegacyPathArgument() throws IOException {
380 final byte header = input.readByte();
381 return switch (header & MagnesiumPathArgument.TYPE_MASK) {
382 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER -> Either.ofFirst(readAugmentationIdentifier(header));
383 case MagnesiumPathArgument.NODE_IDENTIFIER -> {
384 verifyPathIdentifierOnly(header);
385 yield Either.ofFirst(readNodeIdentifier(header));
387 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES ->
388 Either.ofFirst(readNodeIdentifierWithPredicates(header));
389 case MagnesiumPathArgument.NODE_WITH_VALUE -> {
390 verifyPathIdentifierOnly(header);
391 yield Either.ofFirst(readNodeWithValue(header));
393 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER -> {
394 verifyPathIdentifierOnly(header);
395 yield Either.ofSecond(new LegacyMountPointIdentifier(readNodeIdentifier(header).getNodeType()));
397 default -> throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
401 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
402 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
403 codedAugments.add(result);
407 private @NonNull AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
408 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
409 return switch (count) {
410 case MagnesiumPathArgument.AID_COUNT_1B -> readAugmentationIdentifier(input.readUnsignedByte());
411 case MagnesiumPathArgument.AID_COUNT_2B -> readAugmentationIdentifier(input.readUnsignedShort());
412 case MagnesiumPathArgument.AID_COUNT_4B -> readAugmentationIdentifier(input.readInt());
413 default -> readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
417 private @NonNull AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
419 final List<QName> qnames = new ArrayList<>(size);
420 for (int i = 0; i < size; ++i) {
421 qnames.add(readQName());
423 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
424 } else if (size == 0) {
427 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
431 private @NonNull NodeIdentifier readNodeIdentifier() throws IOException {
432 return decodeNodeIdentifier();
435 private @NonNull NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
436 return switch (header & MagnesiumPathArgument.QNAME_MASK) {
437 case MagnesiumPathArgument.QNAME_DEF -> decodeNodeIdentifier();
438 case MagnesiumPathArgument.QNAME_REF_1B -> decodeNodeIdentifierRef1();
439 case MagnesiumPathArgument.QNAME_REF_2B -> decodeNodeIdentifierRef2();
440 case MagnesiumPathArgument.QNAME_REF_4B -> decodeNodeIdentifierRef4();
441 default -> throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
445 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header)
447 final QName qname = readNodeIdentifier(header).getNodeType();
448 return switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
449 case MagnesiumPathArgument.SIZE_1B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
450 case MagnesiumPathArgument.SIZE_2B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
451 case MagnesiumPathArgument.SIZE_4B -> readNodeIdentifierWithPredicates(qname, input.readInt());
452 default -> readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
456 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
459 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
460 } else if (size > 1) {
461 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
462 for (int i = 0; i < size; ++i) {
463 builder.put(readQName(), readLeafValue());
465 return NodeIdentifierWithPredicates.of(qname, builder.build());
466 } else if (size == 0) {
467 return NodeIdentifierWithPredicates.of(qname);
469 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
473 private @NonNull NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
474 final QName qname = readNodeIdentifier(header).getNodeType();
475 return new NodeWithValue<>(qname, readLeafValue());
478 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
479 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
480 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
484 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
485 return lookupNodeIdentifier(input.readUnsignedByte());
488 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
489 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
492 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
493 return lookupNodeIdentifier(input.readInt());
496 private @NonNull QName decodeQName() throws IOException {
497 return decodeNodeIdentifier().getNodeType();
500 private @NonNull QName decodeQNameRef1() throws IOException {
501 return lookupQName(input.readUnsignedByte());
504 private @NonNull QName decodeQNameRef2() throws IOException {
505 return lookupQName(input.readUnsignedShort() + 256);
508 private @NonNull QName decodeQNameRef4() throws IOException {
509 return lookupQName(input.readInt());
512 private @NonNull QNameModule decodeQNameModule() throws IOException {
513 final byte type = input.readByte();
516 case MagnesiumValue.MODREF_1B:
517 index = input.readUnsignedByte();
519 case MagnesiumValue.MODREF_2B:
520 index = input.readUnsignedShort() + 256;
522 case MagnesiumValue.MODREF_4B:
523 index = input.readInt();
526 return decodeQNameModuleDef(type);
530 return codedModules.get(index);
531 } catch (IndexOutOfBoundsException e) {
532 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
536 // QNameModule definition, i.e. two encoded strings
537 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
538 final String namespace = readRefString(type);
540 final byte refType = input.readByte();
541 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
542 final QNameModule module;
544 module = QNameFactory.createModule(namespace, revision);
545 } catch (UncheckedExecutionException e) {
546 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
550 codedModules.add(module);
554 private @NonNull String readRefString() throws IOException {
555 return readRefString(input.readByte());
558 private @NonNull String readRefString(final byte type) throws IOException {
561 case MagnesiumValue.STRING_REF_1B:
562 return lookupString(input.readUnsignedByte());
563 case MagnesiumValue.STRING_REF_2B:
564 return lookupString(input.readUnsignedShort() + 256);
565 case MagnesiumValue.STRING_REF_4B:
566 return lookupString(input.readInt());
567 case MagnesiumValue.STRING_EMPTY:
569 case MagnesiumValue.STRING_2B:
572 case MagnesiumValue.STRING_4B:
575 case MagnesiumValue.STRING_CHARS:
576 str = readCharsString();
578 case MagnesiumValue.STRING_UTF:
579 str = input.readUTF();
582 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
585 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
586 codedStrings.add(verifyNotNull(str));
590 private @NonNull String readString() throws IOException {
591 final byte type = input.readByte();
592 return switch (type) {
593 case MagnesiumValue.STRING_EMPTY -> "";
594 case MagnesiumValue.STRING_UTF -> input.readUTF();
595 case MagnesiumValue.STRING_2B -> readString2();
596 case MagnesiumValue.STRING_4B -> readString4();
597 case MagnesiumValue.STRING_CHARS -> readCharsString();
598 default -> throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
602 private @NonNull String readString2() throws IOException {
603 return readByteString(input.readUnsignedShort());
606 private @NonNull String readString4() throws IOException {
607 return readByteString(input.readInt());
610 private @NonNull String readByteString(final int size) throws IOException {
612 final byte[] bytes = new byte[size];
613 input.readFully(bytes);
614 return new String(bytes, StandardCharsets.UTF_8);
615 } else if (size == 0) {
618 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
622 private @NonNull String readCharsString() throws IOException {
623 final int size = input.readInt();
625 final char[] chars = new char[size];
626 for (int i = 0; i < size; ++i) {
627 chars[i] = input.readChar();
629 return String.valueOf(chars);
630 } else if (size == 0) {
633 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
637 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
639 return codedNodeIdentifiers.get(index);
640 } catch (IndexOutOfBoundsException e) {
641 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
645 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
646 return lookupNodeIdentifier(index).getNodeType();
649 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
651 return codedStrings.get(index);
652 } catch (IndexOutOfBoundsException e) {
653 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
657 private @NonNull DOMSource readDOMSource() throws IOException {
658 final String str = readString();
660 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
661 .getDocumentElement());
662 } catch (SAXException e) {
663 throw new IOException("Error parsing XML: " + str, e);
667 private @NonNull Object readLeafValue() throws IOException {
668 final byte type = input.readByte();
670 case MagnesiumValue.BOOLEAN_FALSE:
671 return Boolean.FALSE;
672 case MagnesiumValue.BOOLEAN_TRUE:
674 case MagnesiumValue.EMPTY:
675 return Empty.value();
676 case MagnesiumValue.INT8:
677 return input.readByte();
678 case MagnesiumValue.INT8_0:
680 case MagnesiumValue.INT16:
681 return input.readShort();
682 case MagnesiumValue.INT16_0:
684 case MagnesiumValue.INT32:
685 return input.readInt();
686 case MagnesiumValue.INT32_0:
688 case MagnesiumValue.INT32_2B:
689 return input.readShort() & 0xFFFF;
690 case MagnesiumValue.INT64:
691 return input.readLong();
692 case MagnesiumValue.INT64_0:
694 case MagnesiumValue.INT64_4B:
695 return input.readInt() & 0xFFFFFFFFL;
696 case MagnesiumValue.UINT8:
697 return Uint8.fromByteBits(input.readByte());
698 case MagnesiumValue.UINT8_0:
700 case MagnesiumValue.UINT16:
701 return Uint16.fromShortBits(input.readShort());
702 case MagnesiumValue.UINT16_0:
704 case MagnesiumValue.UINT32:
705 return Uint32.fromIntBits(input.readInt());
706 case MagnesiumValue.UINT32_0:
708 case MagnesiumValue.UINT32_2B:
709 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
710 case MagnesiumValue.UINT64:
711 return Uint64.fromLongBits(input.readLong());
712 case MagnesiumValue.UINT64_0:
714 case MagnesiumValue.UINT64_4B:
715 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
716 case MagnesiumValue.BIGDECIMAL:
717 // FIXME: use string -> BigDecimal cache
718 return Decimal64.valueOf(input.readUTF());
719 case MagnesiumValue.BIGINTEGER:
720 return readBigInteger();
721 case MagnesiumValue.STRING_EMPTY:
723 case MagnesiumValue.STRING_UTF:
724 return input.readUTF();
725 case MagnesiumValue.STRING_2B:
726 return readString2();
727 case MagnesiumValue.STRING_4B:
728 return readString4();
729 case MagnesiumValue.STRING_CHARS:
730 return readCharsString();
731 case MagnesiumValue.BINARY_0:
733 case MagnesiumValue.BINARY_1B:
734 return readBinary(128 + input.readUnsignedByte());
735 case MagnesiumValue.BINARY_2B:
736 return readBinary(384 + input.readUnsignedShort());
737 case MagnesiumValue.BINARY_4B:
738 return readBinary(input.readInt());
739 case MagnesiumValue.YIID_0:
740 return YangInstanceIdentifier.empty();
741 case MagnesiumValue.YIID:
742 return readYangInstanceIdentifier(input.readInt());
743 case MagnesiumValue.QNAME:
744 return decodeQName();
745 case MagnesiumValue.QNAME_REF_1B:
746 return decodeQNameRef1();
747 case MagnesiumValue.QNAME_REF_2B:
748 return decodeQNameRef2();
749 case MagnesiumValue.QNAME_REF_4B:
750 return decodeQNameRef4();
751 case MagnesiumValue.BITS_0:
752 return ImmutableSet.of();
753 case MagnesiumValue.BITS_1B:
754 return readBits(input.readUnsignedByte() + 29);
755 case MagnesiumValue.BITS_2B:
756 return readBits(input.readUnsignedShort() + 285);
757 case MagnesiumValue.BITS_4B:
758 return readBits(input.readInt());
761 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
762 return readBinary(type - MagnesiumValue.BINARY_0);
763 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
764 return readBits(type - MagnesiumValue.BITS_0);
765 } else if (type > MagnesiumValue.YIID_0) {
766 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
767 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
769 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
774 abstract @NonNull BigInteger readBigInteger() throws IOException;
776 private byte @NonNull [] readBinary(final int size) throws IOException {
778 final byte[] ret = new byte[size];
779 input.readFully(ret);
781 } else if (size == 0) {
784 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
788 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
790 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
791 for (int i = 0; i < size; ++i) {
792 builder.add(readRefString());
794 return builder.build();
795 } else if (size == 0) {
796 return ImmutableSet.of();
798 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
802 private static byte mask(final byte header, final byte mask) {
803 return (byte) (header & mask);
806 private static int rshift(final byte header, final byte shift) {
807 return (header & 0xFF) >>> shift;