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.rfc8528.data.api.MountPointIdentifier;
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 public final PathArgument readPathArgument() throws IOException {
379 final byte header = input.readByte();
380 return switch (header & MagnesiumPathArgument.TYPE_MASK) {
381 case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER -> readAugmentationIdentifier(header);
382 case MagnesiumPathArgument.NODE_IDENTIFIER -> {
383 verifyPathIdentifierOnly(header);
384 yield readNodeIdentifier(header);
386 case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES -> readNodeIdentifierWithPredicates(header);
387 case MagnesiumPathArgument.NODE_WITH_VALUE -> {
388 verifyPathIdentifierOnly(header);
389 yield readNodeWithValue(header);
391 case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER -> {
392 verifyPathIdentifierOnly(header);
393 yield MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
395 default -> throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
399 private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
400 final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
401 codedAugments.add(result);
405 private @NonNull AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
406 final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
407 return switch (count) {
408 case MagnesiumPathArgument.AID_COUNT_1B -> readAugmentationIdentifier(input.readUnsignedByte());
409 case MagnesiumPathArgument.AID_COUNT_2B -> readAugmentationIdentifier(input.readUnsignedShort());
410 case MagnesiumPathArgument.AID_COUNT_4B -> readAugmentationIdentifier(input.readInt());
411 default -> readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
415 private @NonNull AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
417 final List<QName> qnames = new ArrayList<>(size);
418 for (int i = 0; i < size; ++i) {
419 qnames.add(readQName());
421 return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
422 } else if (size == 0) {
425 throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
429 private @NonNull NodeIdentifier readNodeIdentifier() throws IOException {
430 return decodeNodeIdentifier();
433 private @NonNull NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
434 return switch (header & MagnesiumPathArgument.QNAME_MASK) {
435 case MagnesiumPathArgument.QNAME_DEF -> decodeNodeIdentifier();
436 case MagnesiumPathArgument.QNAME_REF_1B -> decodeNodeIdentifierRef1();
437 case MagnesiumPathArgument.QNAME_REF_2B -> decodeNodeIdentifierRef2();
438 case MagnesiumPathArgument.QNAME_REF_4B -> decodeNodeIdentifierRef4();
439 default -> throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
443 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header)
445 final QName qname = readNodeIdentifier(header).getNodeType();
446 return switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
447 case MagnesiumPathArgument.SIZE_1B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
448 case MagnesiumPathArgument.SIZE_2B -> readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
449 case MagnesiumPathArgument.SIZE_4B -> readNodeIdentifierWithPredicates(qname, input.readInt());
450 default -> readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
454 private @NonNull NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
457 return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
458 } else if (size > 1) {
459 final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
460 for (int i = 0; i < size; ++i) {
461 builder.put(readQName(), readLeafValue());
463 return NodeIdentifierWithPredicates.of(qname, builder.build());
464 } else if (size == 0) {
465 return NodeIdentifierWithPredicates.of(qname);
467 throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
471 private @NonNull NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
472 final QName qname = readNodeIdentifier(header).getNodeType();
473 return new NodeWithValue<>(qname, readLeafValue());
476 private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
477 if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
478 throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
482 private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
483 return lookupNodeIdentifier(input.readUnsignedByte());
486 private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
487 return lookupNodeIdentifier(input.readUnsignedShort() + 256);
490 private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
491 return lookupNodeIdentifier(input.readInt());
494 private @NonNull QName decodeQName() throws IOException {
495 return decodeNodeIdentifier().getNodeType();
498 private @NonNull QName decodeQNameRef1() throws IOException {
499 return lookupQName(input.readUnsignedByte());
502 private @NonNull QName decodeQNameRef2() throws IOException {
503 return lookupQName(input.readUnsignedShort() + 256);
506 private @NonNull QName decodeQNameRef4() throws IOException {
507 return lookupQName(input.readInt());
510 private @NonNull QNameModule decodeQNameModule() throws IOException {
511 final byte type = input.readByte();
514 case MagnesiumValue.MODREF_1B:
515 index = input.readUnsignedByte();
517 case MagnesiumValue.MODREF_2B:
518 index = input.readUnsignedShort() + 256;
520 case MagnesiumValue.MODREF_4B:
521 index = input.readInt();
524 return decodeQNameModuleDef(type);
528 return codedModules.get(index);
529 } catch (IndexOutOfBoundsException e) {
530 throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
534 // QNameModule definition, i.e. two encoded strings
535 private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
536 final String namespace = readRefString(type);
538 final byte refType = input.readByte();
539 final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
540 final QNameModule module;
542 module = QNameFactory.createModule(namespace, revision);
543 } catch (UncheckedExecutionException e) {
544 throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
548 codedModules.add(module);
552 private @NonNull String readRefString() throws IOException {
553 return readRefString(input.readByte());
556 private @NonNull String readRefString(final byte type) throws IOException {
559 case MagnesiumValue.STRING_REF_1B:
560 return lookupString(input.readUnsignedByte());
561 case MagnesiumValue.STRING_REF_2B:
562 return lookupString(input.readUnsignedShort() + 256);
563 case MagnesiumValue.STRING_REF_4B:
564 return lookupString(input.readInt());
565 case MagnesiumValue.STRING_EMPTY:
567 case MagnesiumValue.STRING_2B:
570 case MagnesiumValue.STRING_4B:
573 case MagnesiumValue.STRING_CHARS:
574 str = readCharsString();
576 case MagnesiumValue.STRING_UTF:
577 str = input.readUTF();
580 throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
583 // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
584 codedStrings.add(verifyNotNull(str));
588 private @NonNull String readString() throws IOException {
589 final byte type = input.readByte();
590 return switch (type) {
591 case MagnesiumValue.STRING_EMPTY -> "";
592 case MagnesiumValue.STRING_UTF -> input.readUTF();
593 case MagnesiumValue.STRING_2B -> readString2();
594 case MagnesiumValue.STRING_4B -> readString4();
595 case MagnesiumValue.STRING_CHARS -> readCharsString();
596 default -> throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
600 private @NonNull String readString2() throws IOException {
601 return readByteString(input.readUnsignedShort());
604 private @NonNull String readString4() throws IOException {
605 return readByteString(input.readInt());
608 private @NonNull String readByteString(final int size) throws IOException {
610 final byte[] bytes = new byte[size];
611 input.readFully(bytes);
612 return new String(bytes, StandardCharsets.UTF_8);
613 } else if (size == 0) {
616 throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
620 private @NonNull String readCharsString() throws IOException {
621 final int size = input.readInt();
623 final char[] chars = new char[size];
624 for (int i = 0; i < size; ++i) {
625 chars[i] = input.readChar();
627 return String.valueOf(chars);
628 } else if (size == 0) {
631 throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
635 private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
637 return codedNodeIdentifiers.get(index);
638 } catch (IndexOutOfBoundsException e) {
639 throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
643 private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
644 return lookupNodeIdentifier(index).getNodeType();
647 private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
649 return codedStrings.get(index);
650 } catch (IndexOutOfBoundsException e) {
651 throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
655 private @NonNull DOMSource readDOMSource() throws IOException {
656 final String str = readString();
658 return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
659 .getDocumentElement());
660 } catch (SAXException e) {
661 throw new IOException("Error parsing XML: " + str, e);
665 private @NonNull Object readLeafValue() throws IOException {
666 final byte type = input.readByte();
668 case MagnesiumValue.BOOLEAN_FALSE:
669 return Boolean.FALSE;
670 case MagnesiumValue.BOOLEAN_TRUE:
672 case MagnesiumValue.EMPTY:
673 return Empty.value();
674 case MagnesiumValue.INT8:
675 return input.readByte();
676 case MagnesiumValue.INT8_0:
678 case MagnesiumValue.INT16:
679 return input.readShort();
680 case MagnesiumValue.INT16_0:
682 case MagnesiumValue.INT32:
683 return input.readInt();
684 case MagnesiumValue.INT32_0:
686 case MagnesiumValue.INT32_2B:
687 return input.readShort() & 0xFFFF;
688 case MagnesiumValue.INT64:
689 return input.readLong();
690 case MagnesiumValue.INT64_0:
692 case MagnesiumValue.INT64_4B:
693 return input.readInt() & 0xFFFFFFFFL;
694 case MagnesiumValue.UINT8:
695 return Uint8.fromByteBits(input.readByte());
696 case MagnesiumValue.UINT8_0:
698 case MagnesiumValue.UINT16:
699 return Uint16.fromShortBits(input.readShort());
700 case MagnesiumValue.UINT16_0:
702 case MagnesiumValue.UINT32:
703 return Uint32.fromIntBits(input.readInt());
704 case MagnesiumValue.UINT32_0:
706 case MagnesiumValue.UINT32_2B:
707 return Uint32.fromIntBits(input.readShort() & 0xFFFF);
708 case MagnesiumValue.UINT64:
709 return Uint64.fromLongBits(input.readLong());
710 case MagnesiumValue.UINT64_0:
712 case MagnesiumValue.UINT64_4B:
713 return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
714 case MagnesiumValue.BIGDECIMAL:
715 // FIXME: use string -> BigDecimal cache
716 return Decimal64.valueOf(input.readUTF());
717 case MagnesiumValue.BIGINTEGER:
718 return readBigInteger();
719 case MagnesiumValue.STRING_EMPTY:
721 case MagnesiumValue.STRING_UTF:
722 return input.readUTF();
723 case MagnesiumValue.STRING_2B:
724 return readString2();
725 case MagnesiumValue.STRING_4B:
726 return readString4();
727 case MagnesiumValue.STRING_CHARS:
728 return readCharsString();
729 case MagnesiumValue.BINARY_0:
731 case MagnesiumValue.BINARY_1B:
732 return readBinary(128 + input.readUnsignedByte());
733 case MagnesiumValue.BINARY_2B:
734 return readBinary(384 + input.readUnsignedShort());
735 case MagnesiumValue.BINARY_4B:
736 return readBinary(input.readInt());
737 case MagnesiumValue.YIID_0:
738 return YangInstanceIdentifier.empty();
739 case MagnesiumValue.YIID:
740 return readYangInstanceIdentifier(input.readInt());
741 case MagnesiumValue.QNAME:
742 return decodeQName();
743 case MagnesiumValue.QNAME_REF_1B:
744 return decodeQNameRef1();
745 case MagnesiumValue.QNAME_REF_2B:
746 return decodeQNameRef2();
747 case MagnesiumValue.QNAME_REF_4B:
748 return decodeQNameRef4();
749 case MagnesiumValue.BITS_0:
750 return ImmutableSet.of();
751 case MagnesiumValue.BITS_1B:
752 return readBits(input.readUnsignedByte() + 29);
753 case MagnesiumValue.BITS_2B:
754 return readBits(input.readUnsignedShort() + 285);
755 case MagnesiumValue.BITS_4B:
756 return readBits(input.readInt());
759 if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
760 return readBinary(type - MagnesiumValue.BINARY_0);
761 } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
762 return readBits(type - MagnesiumValue.BITS_0);
763 } else if (type > MagnesiumValue.YIID_0) {
764 // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
765 return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
767 throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
772 abstract @NonNull BigInteger readBigInteger() throws IOException;
774 private byte @NonNull [] readBinary(final int size) throws IOException {
776 final byte[] ret = new byte[size];
777 input.readFully(ret);
779 } else if (size == 0) {
782 throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
786 private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
788 final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
789 for (int i = 0; i < size; ++i) {
790 builder.add(readRefString());
792 return builder.build();
793 } else if (size == 0) {
794 return ImmutableSet.of();
796 throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
800 private static byte mask(final byte header, final byte mask) {
801 return (byte) (header & mask);
804 private static int rshift(final byte header, final byte shift) {
805 return (header & 0xFF) >>> shift;