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.NodeIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
44 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.xml.sax.InputSource;
48 import org.xml.sax.SAXException;
51 * Abstract base class for NormalizedNodeDataInput based on {@link MagnesiumNode}, {@link MagnesiumPathArgument} and
52 * {@link MagnesiumValue}.
54 abstract class AbstractMagnesiumDataInput extends AbstractNormalizedNodeDataInput {
55 private static final Logger LOG = LoggerFactory.getLogger(AbstractMagnesiumDataInput.class);
57 // Known singleton objects
58 private static final @NonNull Byte INT8_0 = 0;
59 private static final @NonNull Short INT16_0 = 0;
60 private static final @NonNull Integer INT32_0 = 0;
61 private static final @NonNull Long INT64_0 = 0L;
62 private static final byte @NonNull[] BINARY_0 = new byte[0];
63 private static final @NonNull LegacyAugmentationIdentifier EMPTY_AID =
64 new LegacyAugmentationIdentifier(ImmutableSet.of());
66 private final List<LegacyAugmentationIdentifier> 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 Object 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 var augIdentifier = decodeAugmentationIdentifier(nodeHeader);
140 LOG.trace("Streaming augmentation node {}", augIdentifier);
141 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
142 streamNormalizedNode(writer, augIdentifier, nodeType);
146 private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
147 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
148 LOG.trace("Streaming choice node {}", identifier);
149 writer.startChoiceNode(identifier, UNKNOWN_SIZE);
150 commonStreamContainer(writer, identifier);
153 private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
154 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
155 LOG.trace("Streaming container node {}", identifier);
156 writer.startContainerNode(identifier, UNKNOWN_SIZE);
157 commonStreamContainer(writer, identifier);
160 private void streamLeaf(final NormalizedNodeStreamWriter writer, final Object parent, final byte nodeHeader)
162 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
163 LOG.trace("Streaming leaf node {}", identifier);
164 writer.startLeafNode(identifier);
167 if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
168 if (!(parent instanceof NodeIdentifierWithPredicates nip)) {
169 throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
173 value = nip.getValue(identifier.getNodeType());
175 throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
176 + " in parent " + parent);
179 value = readLeafValue();
182 writer.scalarValue(value);
186 private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
187 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
188 LOG.trace("Streaming leaf set node {}", identifier);
189 writer.startLeafSet(identifier, UNKNOWN_SIZE);
190 commonStreamContainer(writer, identifier);
193 private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
195 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
196 LOG.trace("Streaming ordered leaf set node {}", identifier);
197 writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
199 commonStreamContainer(writer, identifier);
202 private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final Object parent, final byte nodeHeader)
204 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
205 final Object value = readLeafValue();
206 final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
207 LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
208 writer.startLeafSetEntryNode(leafIdentifier);
209 writer.scalarValue(value);
213 private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
214 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
215 writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
216 commonStreamContainer(writer, identifier);
219 private void streamListEntry(final NormalizedNodeStreamWriter writer, final Object parent, final byte nodeHeader)
221 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
222 LOG.trace("Streaming unkeyed list item node {}", identifier);
223 writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
224 commonStreamContainer(writer, identifier);
227 private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
228 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
229 LOG.trace("Streaming map node {}", identifier);
230 writer.startMapNode(identifier, UNKNOWN_SIZE);
231 commonStreamContainer(writer, identifier);
234 private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
235 final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
236 LOG.trace("Streaming ordered map node {}", identifier);
237 writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
238 commonStreamContainer(writer, identifier);
241 private void streamMapEntry(final NormalizedNodeStreamWriter writer, final Object parent, final byte nodeHeader)
243 final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
245 final int size = switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
246 case MagnesiumNode.PREDICATE_ZERO -> 0;
247 case MagnesiumNode.PREDICATE_ONE -> 1;
248 case MagnesiumNode.PREDICATE_1B -> input.readUnsignedByte();
249 case MagnesiumNode.PREDICATE_4B -> input.readInt();
251 // ISE on purpose: this should never ever happen
252 throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
255 final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
256 LOG.trace("Streaming map entry node {}", identifier);
257 writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
258 commonStreamContainer(writer, identifier);
261 private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
263 for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
264 streamNormalizedNode(writer, parent, nodeType);
269 private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
270 final QNameModule module = decodeQNameModule();
271 final String localName = readRefString();
272 final NodeIdentifier nodeId;
274 nodeId = QNameFactory.getNodeIdentifier(module, localName);
275 } catch (ExecutionException e) {
276 throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
280 codedNodeIdentifiers.add(nodeId);
284 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
285 return decodeNodeIdentifier(nodeHeader, null);
288 private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final Object parent) throws IOException {
290 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
291 case MagnesiumNode.ADDR_DEFINE:
292 return readNodeIdentifier();
293 case MagnesiumNode.ADDR_LOOKUP_1B:
294 index = input.readUnsignedByte();
296 case MagnesiumNode.ADDR_LOOKUP_4B:
297 index = input.readInt();
299 case MagnesiumNode.ADDR_PARENT:
300 if (parent instanceof NodeIdentifier nid) {
303 throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
305 throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
310 return codedNodeIdentifiers.get(index);
311 } catch (IndexOutOfBoundsException e) {
312 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
316 private LegacyAugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
318 switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
319 case MagnesiumNode.ADDR_DEFINE:
320 return readAugmentationIdentifier();
321 case MagnesiumNode.ADDR_LOOKUP_1B:
322 index = input.readUnsignedByte();
324 case MagnesiumNode.ADDR_LOOKUP_4B:
325 index = input.readInt();
328 throw new InvalidNormalizedNodeStreamException(
329 "Unexpected augmentation identifier addressing in header " + nodeHeader);
333 return codedAugments.get(index);
334 } catch (IndexOutOfBoundsException e) {
335 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
340 public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
341 final byte type = input.readByte();
342 if (type == MagnesiumValue.YIID) {
343 return readYangInstanceIdentifier(input.readInt());
344 } else if (type >= MagnesiumValue.YIID_0) {
345 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
346 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
348 throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
352 private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
354 final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
355 for (int i = 0; i < size; ++i) {
356 builder.add(readPathArgument());
358 return YangInstanceIdentifier.create(builder.build());
359 } else if (size == 0) {
360 return YangInstanceIdentifier.empty();
362 throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
367 public final QName readQName() throws IOException {
368 final byte type = input.readByte();
369 return switch (type) {
370 case MagnesiumValue.QNAME -> decodeQName();
371 case MagnesiumValue.QNAME_REF_1B -> decodeQNameRef1();
372 case MagnesiumValue.QNAME_REF_2B -> decodeQNameRef2();
373 case MagnesiumValue.QNAME_REF_4B -> decodeQNameRef4();
374 default -> throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
379 @Deprecated(since = "11.0.0", forRemoval = true)
380 public final Either<PathArgument, LegacyPathArgument> readLegacyPathArgument() throws IOException {
381 final byte header = input.readByte();
382 return switch (header & MagnesiumPathArgument.TYPE_MASK) {
383 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER -> Either.ofSecond(readAugmentationIdentifier(header));
384 case MagnesiumPathArgument.NODE_IDENTIFIER -> {
385 verifyPathIdentifierOnly(header);
386 yield Either.ofFirst(readNodeIdentifier(header));
388 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES ->
389 Either.ofFirst(readNodeIdentifierWithPredicates(header));
390 case MagnesiumPathArgument.NODE_WITH_VALUE -> {
391 verifyPathIdentifierOnly(header);
392 yield Either.ofFirst(readNodeWithValue(header));
394 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER -> {
395 verifyPathIdentifierOnly(header);
396 yield Either.ofSecond(new LegacyMountPointIdentifier(readNodeIdentifier(header).getNodeType()));
398 default -> throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
402 private @NonNull LegacyAugmentationIdentifier readAugmentationIdentifier() throws IOException {
403 final var result = readAugmentationIdentifier(input.readInt());
404 codedAugments.add(result);
408 private @NonNull LegacyAugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
409 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
410 return switch (count) {
411 case MagnesiumPathArgument.AID_COUNT_1B -> readAugmentationIdentifier(input.readUnsignedByte());
412 case MagnesiumPathArgument.AID_COUNT_2B -> readAugmentationIdentifier(input.readUnsignedShort());
413 case MagnesiumPathArgument.AID_COUNT_4B -> readAugmentationIdentifier(input.readInt());
414 default -> readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
418 private @NonNull LegacyAugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
420 final var qnames = ImmutableSet.<QName>builderWithExpectedSize(size);
421 for (int i = 0; i < size; ++i) {
422 qnames.add(readQName());
424 return new LegacyAugmentationIdentifier(qnames.build());
425 } else if (size == 0) {
428 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
432 private @NonNull NodeIdentifier readNodeIdentifier() throws IOException {
433 return decodeNodeIdentifier();
436 private @NonNull NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
437 return switch (header & MagnesiumPathArgument.QNAME_MASK) {
438 case MagnesiumPathArgument.QNAME_DEF -> decodeNodeIdentifier();
439 case MagnesiumPathArgument.QNAME_REF_1B -> decodeNodeIdentifierRef1();
440 case MagnesiumPathArgument.QNAME_REF_2B -> decodeNodeIdentifierRef2();
441 case MagnesiumPathArgument.QNAME_REF_4B -> decodeNodeIdentifierRef4();
442 default -> throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
446 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header)
448 final QName qname = readNodeIdentifier(header).getNodeType();
449 return switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
450 case MagnesiumPathArgument.SIZE_1B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
451 case MagnesiumPathArgument.SIZE_2B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
452 case MagnesiumPathArgument.SIZE_4B -> readNodeIdentifierWithPredicates(qname, input.readInt());
453 default -> readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
457 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
460 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
461 } else if (size > 1) {
462 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
463 for (int i = 0; i < size; ++i) {
464 builder.put(readQName(), readLeafValue());
466 return NodeIdentifierWithPredicates.of(qname, builder.build());
467 } else if (size == 0) {
468 return NodeIdentifierWithPredicates.of(qname);
470 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
474 private @NonNull NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
475 final QName qname = readNodeIdentifier(header).getNodeType();
476 return new NodeWithValue<>(qname, readLeafValue());
479 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
480 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
481 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
485 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
486 return lookupNodeIdentifier(input.readUnsignedByte());
489 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
490 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
493 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
494 return lookupNodeIdentifier(input.readInt());
497 private @NonNull QName decodeQName() throws IOException {
498 return decodeNodeIdentifier().getNodeType();
501 private @NonNull QName decodeQNameRef1() throws IOException {
502 return lookupQName(input.readUnsignedByte());
505 private @NonNull QName decodeQNameRef2() throws IOException {
506 return lookupQName(input.readUnsignedShort() + 256);
509 private @NonNull QName decodeQNameRef4() throws IOException {
510 return lookupQName(input.readInt());
513 private @NonNull QNameModule decodeQNameModule() throws IOException {
514 final byte type = input.readByte();
517 case MagnesiumValue.MODREF_1B:
518 index = input.readUnsignedByte();
520 case MagnesiumValue.MODREF_2B:
521 index = input.readUnsignedShort() + 256;
523 case MagnesiumValue.MODREF_4B:
524 index = input.readInt();
527 return decodeQNameModuleDef(type);
531 return codedModules.get(index);
532 } catch (IndexOutOfBoundsException e) {
533 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
537 // QNameModule definition, i.e. two encoded strings
538 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
539 final String namespace = readRefString(type);
541 final byte refType = input.readByte();
542 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
543 final QNameModule module;
545 module = QNameFactory.createModule(namespace, revision);
546 } catch (UncheckedExecutionException e) {
547 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
551 codedModules.add(module);
555 private @NonNull String readRefString() throws IOException {
556 return readRefString(input.readByte());
559 private @NonNull String readRefString(final byte type) throws IOException {
562 case MagnesiumValue.STRING_REF_1B:
563 return lookupString(input.readUnsignedByte());
564 case MagnesiumValue.STRING_REF_2B:
565 return lookupString(input.readUnsignedShort() + 256);
566 case MagnesiumValue.STRING_REF_4B:
567 return lookupString(input.readInt());
568 case MagnesiumValue.STRING_EMPTY:
570 case MagnesiumValue.STRING_2B:
573 case MagnesiumValue.STRING_4B:
576 case MagnesiumValue.STRING_CHARS:
577 str = readCharsString();
579 case MagnesiumValue.STRING_UTF:
580 str = input.readUTF();
583 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
586 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
587 codedStrings.add(verifyNotNull(str));
591 private @NonNull String readString() throws IOException {
592 final byte type = input.readByte();
593 return switch (type) {
594 case MagnesiumValue.STRING_EMPTY -> "";
595 case MagnesiumValue.STRING_UTF -> input.readUTF();
596 case MagnesiumValue.STRING_2B -> readString2();
597 case MagnesiumValue.STRING_4B -> readString4();
598 case MagnesiumValue.STRING_CHARS -> readCharsString();
599 default -> throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
603 private @NonNull String readString2() throws IOException {
604 return readByteString(input.readUnsignedShort());
607 private @NonNull String readString4() throws IOException {
608 return readByteString(input.readInt());
611 private @NonNull String readByteString(final int size) throws IOException {
613 final byte[] bytes = new byte[size];
614 input.readFully(bytes);
615 return new String(bytes, StandardCharsets.UTF_8);
616 } else if (size == 0) {
619 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
623 private @NonNull String readCharsString() throws IOException {
624 final int size = input.readInt();
626 final char[] chars = new char[size];
627 for (int i = 0; i < size; ++i) {
628 chars[i] = input.readChar();
630 return String.valueOf(chars);
631 } else if (size == 0) {
634 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
638 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
640 return codedNodeIdentifiers.get(index);
641 } catch (IndexOutOfBoundsException e) {
642 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
646 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
647 return lookupNodeIdentifier(index).getNodeType();
650 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
652 return codedStrings.get(index);
653 } catch (IndexOutOfBoundsException e) {
654 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
658 private @NonNull DOMSource readDOMSource() throws IOException {
659 final String str = readString();
661 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
662 .getDocumentElement());
663 } catch (SAXException e) {
664 throw new IOException("Error parsing XML: " + str, e);
668 private @NonNull Object readLeafValue() throws IOException {
669 final byte type = input.readByte();
671 case MagnesiumValue.BOOLEAN_FALSE:
672 return Boolean.FALSE;
673 case MagnesiumValue.BOOLEAN_TRUE:
675 case MagnesiumValue.EMPTY:
676 return Empty.value();
677 case MagnesiumValue.INT8:
678 return input.readByte();
679 case MagnesiumValue.INT8_0:
681 case MagnesiumValue.INT16:
682 return input.readShort();
683 case MagnesiumValue.INT16_0:
685 case MagnesiumValue.INT32:
686 return input.readInt();
687 case MagnesiumValue.INT32_0:
689 case MagnesiumValue.INT32_2B:
690 return input.readShort() & 0xFFFF;
691 case MagnesiumValue.INT64:
692 return input.readLong();
693 case MagnesiumValue.INT64_0:
695 case MagnesiumValue.INT64_4B:
696 return input.readInt() & 0xFFFFFFFFL;
697 case MagnesiumValue.UINT8:
698 return Uint8.fromByteBits(input.readByte());
699 case MagnesiumValue.UINT8_0:
701 case MagnesiumValue.UINT16:
702 return Uint16.fromShortBits(input.readShort());
703 case MagnesiumValue.UINT16_0:
705 case MagnesiumValue.UINT32:
706 return Uint32.fromIntBits(input.readInt());
707 case MagnesiumValue.UINT32_0:
709 case MagnesiumValue.UINT32_2B:
710 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
711 case MagnesiumValue.UINT64:
712 return Uint64.fromLongBits(input.readLong());
713 case MagnesiumValue.UINT64_0:
715 case MagnesiumValue.UINT64_4B:
716 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
717 case MagnesiumValue.BIGDECIMAL:
718 // FIXME: use string -> BigDecimal cache
719 return Decimal64.valueOf(input.readUTF());
720 case MagnesiumValue.BIGINTEGER:
721 return readBigInteger();
722 case MagnesiumValue.STRING_EMPTY:
724 case MagnesiumValue.STRING_UTF:
725 return input.readUTF();
726 case MagnesiumValue.STRING_2B:
727 return readString2();
728 case MagnesiumValue.STRING_4B:
729 return readString4();
730 case MagnesiumValue.STRING_CHARS:
731 return readCharsString();
732 case MagnesiumValue.BINARY_0:
734 case MagnesiumValue.BINARY_1B:
735 return readBinary(128 + input.readUnsignedByte());
736 case MagnesiumValue.BINARY_2B:
737 return readBinary(384 + input.readUnsignedShort());
738 case MagnesiumValue.BINARY_4B:
739 return readBinary(input.readInt());
740 case MagnesiumValue.YIID_0:
741 return YangInstanceIdentifier.empty();
742 case MagnesiumValue.YIID:
743 return readYangInstanceIdentifier(input.readInt());
744 case MagnesiumValue.QNAME:
745 return decodeQName();
746 case MagnesiumValue.QNAME_REF_1B:
747 return decodeQNameRef1();
748 case MagnesiumValue.QNAME_REF_2B:
749 return decodeQNameRef2();
750 case MagnesiumValue.QNAME_REF_4B:
751 return decodeQNameRef4();
752 case MagnesiumValue.BITS_0:
753 return ImmutableSet.of();
754 case MagnesiumValue.BITS_1B:
755 return readBits(input.readUnsignedByte() + 29);
756 case MagnesiumValue.BITS_2B:
757 return readBits(input.readUnsignedShort() + 285);
758 case MagnesiumValue.BITS_4B:
759 return readBits(input.readInt());
762 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
763 return readBinary(type - MagnesiumValue.BINARY_0);
764 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
765 return readBits(type - MagnesiumValue.BITS_0);
766 } else if (type > MagnesiumValue.YIID_0) {
767 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
768 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
770 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
775 abstract @NonNull BigInteger readBigInteger() throws IOException;
777 private byte @NonNull [] readBinary(final int size) throws IOException {
779 final byte[] ret = new byte[size];
780 input.readFully(ret);
782 } else if (size == 0) {
785 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
789 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
791 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
792 for (int i = 0; i < size; ++i) {
793 builder.add(readRefString());
795 return builder.build();
796 } else if (size == 0) {
797 return ImmutableSet.of();
799 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
803 private static byte mask(final byte header, final byte mask) {
804 return (byte) (header & mask);
807 private static int rshift(final byte header, final byte shift) {
808 return (header & 0xFF) >>> shift;