--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+/**
+ * Simplistic coding, without any real magic bit reuse. The idea is that each statement is in the form:
+ * <pre>{@code HEADER LINE COLUMN KEYWORD [ARGUMENT] [SUBSTATEMENTS]}</pre>
+ *
+ * <p>
+ * The {@code HEADER} is always a single byte, internally composed of four bitfields:
+ * <pre>
+ * +---+---+---+---+---+---+---+---+
+ * | KW TYPE | SIZE |ARG| L6N |
+ * +---+---+---+---+---+---+---+---+
+ * </pre>
+ *
+ * <p>
+ * The {@code LINE} and {@code COLUMN} are variable size, indicated by the {@code L6N} bits:
+ * <ul>
+ * <li>{@link #HDR_LOCATION_22} indicates u16 for LINE and u16 for COLUMN</li>
+ * <li>{@link #HDR_LOCATION_31} indicates u24 for LINE and u8 for COLUMN</li>
+ * <li>{@link #HDR_LOCATION_44} indicates s32 for LINE and s32 for COLUMN</li>
+ * </ul>
+ *
+ * <p>
+ * The {@code KEYWORD} is variable-format based on {@code KW TYPE} bits:
+ * <ul>
+ * <li>{@link #HDR_KEY_DEF_QUAL} indicates a new definition, which is composed of two {@code STRING}s</li>
+ * <li>{@link #HDR_KEY_DEF_UQUAL} indicates a new definition, which is composed of a single {@code STRING}</li>
+ * <li>{@link #HDR_KEY_REF_U8} indicates a reference identified by a u8 integer</li>
+ * <li>{@link #HDR_KEY_REF_U16} indicates a reference identified by a u16 integer</li>
+ * <li>{@link #HDR_KEY_REF_S32} indicates a reference identified by a s32 integer</li>
+ * </ul>
+ * Once defined, each keyword can be referenced by encoding a reference with a linear counter of definition. I.e.
+ * the first definition is {@code 0}, the second is {@code 1}, etc.
+ *
+ * <p>
+ * The {@code ARGUMENT} is present only when indicated by {@link #HDR_ARGUMENT_PRESENT}. If it is present, it has
+ * variable encoding in form
+ * <pre>{@code ARGHDR [...]}</pre>
+ */
+final class IOConstantsV1 {
+ // Statement indicator: indicates line/column split
+ static final int HDR_LOCATION_22 = 0x01;
+ static final int HDR_LOCATION_31 = 0x02;
+ static final int HDR_LOCATION_44 = 0x03;
+ static final int HDR_LOCATION_MASK = HDR_LOCATION_44;
+ // Argument presence
+ static final int HDR_ARGUMENT_ABSENT = 0x00;
+ static final int HDR_ARGUMENT_PRESENT = 0x04;
+ static final int HDR_ARGUMENT_MASK = HDR_ARGUMENT_PRESENT;
+ // Child statement size
+ static final int HDR_SIZE_0 = 0x00;
+ static final int HDR_SIZE_U8 = 0x08;
+ static final int HDR_SIZE_U16 = 0x10;
+ static final int HDR_SIZE_S32 = 0x18;
+ static final int HDR_SIZE_MASK = HDR_SIZE_S32;
+ // Keyword indication
+ static final int HDR_KEY_DEF_UQUAL = 0x00;
+ static final int HDR_KEY_DEF_QUAL = 0x20;
+ // 0x40 reserved
+ // 0x60 reserved
+ // 0x80 reserved
+ static final int HDR_KEY_REF_U8 = 0xA0;
+ static final int HDR_KEY_REF_U16 = 0xC0;
+ static final int HDR_KEY_REF_S32 = 0xE0;
+ static final int HDR_KEY_MASK = HDR_KEY_REF_S32;
+
+ static final int ARG_TYPE_IDENTIFIER = 0x01;
+ static final int ARG_TYPE_DQUOT = 0x02;
+ static final int ARG_TYPE_SQUOT = 0x03;
+ static final int ARG_TYPE_UQUOT = 0x04;
+ static final int ARG_TYPE_CONCAT_U8 = 0x05;
+ static final int ARG_TYPE_CONCAT_U16 = 0x06;
+ static final int ARG_TYPE_CONCAT_S32 = 0x07;
+ static final int ARG_TYPE_MASK = ARG_TYPE_CONCAT_S32;
+
+ static final int STR_DEF_UTF = 0x00; // writeUTF(), <16384
+ static final int STR_DEF_U8 = 0x10; // byte + UTF
+ static final int STR_DEF_U16 = 0x20; // short + UTF
+ static final int STR_DEF_S32 = 0x30; // int + UTF
+ static final int STR_DEF_CHARS = 0x40; // writeChars()
+ static final int STR_REF_U8 = 0x50;
+ static final int STR_REF_U16 = 0x60;
+ static final int STR_REF_S32 = 0x70;
+ static final int STR_MASK = STR_REF_S32;
+
+ private IOConstantsV1() {
+ // Hidden on purpose
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+import com.google.common.annotations.Beta;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+
+@Beta
+public final class IOSupport {
+ private static final int MAGICK = 0xAF57BA07;
+
+ private IOSupport() {
+ // Hidden on purpose
+ }
+
+ public static void writeStatement(final DataOutput out, final IRStatement statement) throws IOException {
+ out.writeInt(MAGICK);
+ out.writeByte(1);
+ new StatementOutputV1(out).writeStatement(statement);
+ }
+
+ public static @NonNull IRStatement readStatement(final DataInput in) throws IOException {
+ final int magic = in.readInt();
+ if (magic != MAGICK) {
+ throw new IOException("Unexpected magic " + Integer.toHexString(magic));
+ }
+
+ final int version = in.readUnsignedByte();
+ final var input = switch (version) {
+ case 1 -> new StatementInputV1(in);
+ default -> throw new IOException("Unsupported version " + version);
+ };
+
+ return input.readStatement();
+ }
+}
*/
@Beta
public abstract sealed class IRStatement extends AbstractIRObject {
- private static final class Z22 extends IRStatement {
+ static final class Z22 extends IRStatement {
private final short startLine;
private final short startColumn;
}
}
- private static final class Z31 extends IRStatement {
+ static final class Z31 extends IRStatement {
private final int value;
Z31(final IRKeyword keyword, final IRArgument argument, final int startLine, final int startColumn) {
+ this(keyword, argument, startLine << 8 | startColumn & 0xFF);
+ }
+
+ Z31(final IRKeyword keyword, final IRArgument argument, final int value) {
super(keyword, argument);
- value = startLine << 8 | startColumn & 0xFF;
+ this.value = value;
}
@Override
public int startColumn() {
return value & 0xFF;
}
+
+ int value() {
+ return value;
+ }
}
- private static sealed class Z44 extends IRStatement permits O44, L44 {
+ static sealed class Z44 extends IRStatement permits O44, L44 {
private final int startLine;
private final int startColumn;
}
}
- private static final class O44 extends Z44 {
+ static final class O44 extends Z44 {
private final @NonNull IRStatement statement;
O44(final IRKeyword keyword, final IRArgument argument, final IRStatement statement, final int startLine,
}
}
- private static final class L44 extends Z44 {
+ static final class L44 extends Z44 {
private final @NonNull ImmutableList<IRStatement> statements;
L44(final IRKeyword keyword, final IRArgument argument, final ImmutableList<IRStatement> statements,
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.DataInput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+
+abstract class StatementInput {
+ final DataInput in;
+
+ StatementInput(final DataInput in) {
+ this.in = requireNonNull(in);
+ }
+
+ abstract @NonNull IRStatement readStatement() throws IOException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+import com.google.common.collect.ImmutableList;
+import java.io.DataInput;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.ir.IRArgument.Single;
+import org.opendaylight.yangtools.yang.ir.IRKeyword.Qualified;
+import org.opendaylight.yangtools.yang.ir.IRKeyword.Unqualified;
+
+final class StatementInputV1 extends StatementInput {
+ private final List<@NonNull IRKeyword> keywords = new ArrayList<>();
+ private final List<@NonNull String> strings = new ArrayList<>();
+
+ StatementInputV1(final DataInput in) {
+ super(in);
+ }
+
+ @Override
+ IRStatement readStatement() throws IOException {
+ final int header = in.readUnsignedByte();
+ final int locationBits = header & IOConstantsV1.HDR_LOCATION_MASK;
+ return switch (locationBits) {
+ case IOConstantsV1.HDR_LOCATION_22 -> {
+ final int startLine = in.readUnsignedShort();
+ final int startColumn = in.readUnsignedShort();
+ yield new IRStatement.Z22(readKeyword(header), readArgument(header), startLine, startColumn);
+ }
+ case IOConstantsV1.HDR_LOCATION_31 -> {
+ final int value = in.readInt();
+ yield new IRStatement.Z31(readKeyword(header), readArgument(header), value);
+ }
+ case IOConstantsV1.HDR_LOCATION_44 -> readStatement(header);
+ default -> throw new IOException("Unhandled location " + Integer.toHexString(locationBits));
+ };
+ }
+
+ private @NonNull IRStatement readStatement(final int header) throws IOException {
+ final int startLine = in.readInt();
+ final int startColumn = in.readInt();
+ final var keyword = readKeyword(header);
+ final var argument = readArgument(header);
+ final var statements = readSubstatements(header);
+
+ return switch (statements.size()) {
+ case 0 -> new IRStatement.Z44(keyword, argument, startLine, startColumn);
+ case 1 -> new IRStatement.O44(keyword, argument, statements.get(0), startLine, startColumn);
+ default -> new IRStatement.L44(keyword, argument, statements, startLine, startColumn);
+ };
+ }
+
+ private @NonNull IRKeyword readKeyword(final int header) throws IOException {
+ final int keyBits = header & IOConstantsV1.HDR_KEY_MASK;
+ return switch (keyBits) {
+ case IOConstantsV1.HDR_KEY_REF_U8 -> lookupKeyword(in.readUnsignedByte());
+ case IOConstantsV1.HDR_KEY_REF_U16 -> lookupKeyword(in.readUnsignedShort());
+ case IOConstantsV1.HDR_KEY_REF_S32 -> lookupKeyword(in.readInt());
+ case IOConstantsV1.HDR_KEY_DEF_QUAL -> defineKeyword(Qualified.of(readString(), readString()));
+ case IOConstantsV1.HDR_KEY_DEF_UQUAL -> defineKeyword(Unqualified.of(readString()));
+ default -> throw new IllegalStateException("Unhandled key " + Integer.toHexString(keyBits));
+ };
+ }
+
+ private @NonNull IRKeyword lookupKeyword(final int code) throws IOException {
+ try {
+ return keywords.get(code);
+ } catch (IndexOutOfBoundsException e) {
+ throw new IOException("Failed to look up keyword", e);
+ }
+ }
+
+ private @NonNull IRKeyword defineKeyword(final @NonNull IRKeyword keyword) {
+ keywords.add(keyword);
+ return keyword;
+ }
+
+ private ImmutableList<IRStatement> readSubstatements(final int header) throws IOException {
+ final int sizeBits = header & IOConstantsV1.HDR_SIZE_MASK;
+ return switch (sizeBits) {
+ case IOConstantsV1.HDR_SIZE_0 -> ImmutableList.of();
+ case IOConstantsV1.HDR_SIZE_U8 -> readSubstatementList(in.readUnsignedByte());
+ case IOConstantsV1.HDR_SIZE_U16 -> readSubstatementList(in.readUnsignedShort());
+ case IOConstantsV1.HDR_SIZE_S32 -> readSubstatementList(in.readInt());
+ default -> throw new IOException("Unhandled size " + Integer.toHexString(sizeBits));
+ };
+ }
+
+ private ImmutableList<IRStatement> readSubstatementList(final int size) throws IOException {
+ final var builder = ImmutableList.<IRStatement>builderWithExpectedSize(size);
+ for (int i = 0; i < size; ++i) {
+ builder.add(readStatement());
+ }
+ return builder.build();
+ }
+
+ private @Nullable IRArgument readArgument(final int header) throws IOException {
+ if ((header & IOConstantsV1.HDR_ARGUMENT_MASK) == IOConstantsV1.HDR_ARGUMENT_ABSENT) {
+ return null;
+ }
+
+ final int argHeader = in.readUnsignedByte();
+ final int type = argHeader & IOConstantsV1.ARG_TYPE_MASK;
+ return switch (type) {
+ case IOConstantsV1.ARG_TYPE_IDENTIFIER -> IRArgument.identifier(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_DQUOT -> IRArgument.doubleQuoted(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_SQUOT -> IRArgument.singleQuoted(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_UQUOT -> IRArgument.unquoted(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_CONCAT_U8 -> readConcatenation(in.readUnsignedByte());
+ case IOConstantsV1.ARG_TYPE_CONCAT_U16 -> readConcatenation(in.readUnsignedShort());
+ case IOConstantsV1.ARG_TYPE_CONCAT_S32 -> readConcatenation(in.readInt());
+ default -> throw new IOException("Unhandled argument " + Integer.toHexString(type));
+ };
+ }
+
+ private @NonNull Single readSingleArgument() throws IOException {
+ final int argHeader = in.readUnsignedByte();
+ final int type = argHeader & IOConstantsV1.ARG_TYPE_MASK;
+ return switch (type) {
+ case IOConstantsV1.ARG_TYPE_IDENTIFIER -> IRArgument.identifier(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_DQUOT -> IRArgument.doubleQuoted(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_SQUOT -> IRArgument.singleQuoted(readString(argHeader));
+ case IOConstantsV1.ARG_TYPE_UQUOT -> IRArgument.unquoted(readString(argHeader));
+ default -> throw new IOException("Unhandled single argument " + Integer.toHexString(type));
+ };
+ }
+
+ private @NonNull IRArgument readConcatenation(final int count) throws IOException {
+ final var builder = ImmutableList.<Single>builderWithExpectedSize(count);
+ for (int i = 0; i < count; ++i) {
+ builder.add(readSingleArgument());
+ }
+ return IRArgument.of(builder.build());
+ }
+
+ private @NonNull String readString() throws IOException {
+ return readString(in.readUnsignedByte());
+ }
+
+ private @NonNull String readString(final int header) throws IOException {
+ final int type = header & IOConstantsV1.STR_MASK;
+ return switch (type) {
+ case IOConstantsV1.STR_DEF_UTF -> defineString(in.readUTF());
+ case IOConstantsV1.STR_DEF_U8 -> defineString(readStringBytes(in.readUnsignedByte()));
+ case IOConstantsV1.STR_DEF_U16 -> defineString(readStringBytes(in.readUnsignedShort()));
+ case IOConstantsV1.STR_DEF_S32 -> defineString(readStringBytes(in.readInt()));
+ case IOConstantsV1.STR_DEF_CHARS -> defineString(readStringChars());
+ case IOConstantsV1.STR_REF_U8 -> lookupString(in.readUnsignedByte());
+ case IOConstantsV1.STR_REF_U16 -> lookupString(in.readUnsignedShort());
+ case IOConstantsV1.STR_REF_S32 -> lookupString(in.readInt());
+ default -> throw new IOException("Unhandled string " + Integer.toHexString(type));
+ };
+ }
+
+ private @NonNull String lookupString(final int offset) throws IOException {
+ try {
+ return strings.get(offset);
+ } catch (IndexOutOfBoundsException e) {
+ throw new IOException("Invalid String reference " + offset, e);
+ }
+ }
+
+ private @NonNull String defineString(final @NonNull String string) {
+ strings.add(string);
+ return string;
+ }
+
+ private @NonNull String readStringBytes(final int size) throws IOException {
+ if (size > 0) {
+ final byte[] bytes = new byte[size];
+ in.readFully(bytes);
+ return new String(bytes, StandardCharsets.UTF_8);
+ } else if (size == 0) {
+ return "";
+ } else {
+ throw new IOException("Invalid String bytes length " + size);
+ }
+ }
+
+ private @NonNull String readStringChars() throws IOException {
+ final int size = in.readInt();
+ if (size > 0) {
+ final char[] chars = new char[size];
+ for (int i = 0; i < size; ++i) {
+ chars[i] = in.readChar();
+ }
+ return String.valueOf(chars);
+ } else if (size == 0) {
+ return "";
+ } else {
+ throw new IOException("Invalid String chars length " + size);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+abstract class StatementOutput {
+ final DataOutput out;
+
+ StatementOutput(final DataOutput out) {
+ this.out = requireNonNull(out);
+ }
+
+ abstract void writeStatement(IRStatement stmt) throws IOException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.ir;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.ir.IRArgument.Concatenation;
+import org.opendaylight.yangtools.yang.ir.IRArgument.Single;
+import org.opendaylight.yangtools.yang.ir.IRKeyword.Qualified;
+
+final class StatementOutputV1 extends StatementOutput {
+ private final Map<IRKeyword, Integer> keywords = new HashMap<>();
+ private final Map<String, Integer> strings = new HashMap<>();
+
+ StatementOutputV1(final DataOutput out) {
+ super(out);
+ }
+
+ @Override
+ void writeStatement(final IRStatement stmt) throws IOException {
+ final List<? extends IRStatement> statements = stmt.statements();
+ final int size = statements.size();
+ final int sizeBits;
+ if (size == 0) {
+ sizeBits = IOConstantsV1.HDR_SIZE_0;
+ } else if (size <= 255) {
+ sizeBits = IOConstantsV1.HDR_SIZE_U8;
+ } else if (size <= 65535) {
+ sizeBits = IOConstantsV1.HDR_SIZE_U16;
+ } else {
+ sizeBits = IOConstantsV1.HDR_SIZE_S32;
+ }
+
+ final IRKeyword keyword = stmt.keyword();
+ final Integer keyCode = keywords.get(keyword);
+ final int keyBits;
+ if (keyCode != null) {
+ final int key = keyCode;
+ if (key <= 255) {
+ keyBits = IOConstantsV1.HDR_KEY_REF_U8;
+ } else if (size <= 65535) {
+ keyBits = IOConstantsV1.HDR_KEY_REF_U16;
+ } else {
+ keyBits = IOConstantsV1.HDR_KEY_REF_S32;
+ }
+ } else {
+ keyBits = keyword instanceof Qualified ? IOConstantsV1.HDR_KEY_DEF_QUAL : IOConstantsV1.HDR_KEY_DEF_UQUAL;
+ }
+
+ final IRArgument argument = stmt.argument();
+ if (stmt instanceof IRStatement.Z22) {
+ writeHeader(keyBits, IOConstantsV1.HDR_LOCATION_22, sizeBits, argument);
+ out.writeShort(stmt.startLine());
+ out.writeShort(stmt.startColumn());
+ } else if (stmt instanceof IRStatement.Z31 z31) {
+ writeHeader(keyBits, IOConstantsV1.HDR_LOCATION_31, sizeBits, argument);
+ out.writeInt(z31.value());
+ } else {
+ writeHeader(keyBits, IOConstantsV1.HDR_LOCATION_44, sizeBits, argument);
+ out.writeInt(stmt.startLine());
+ out.writeInt(stmt.startColumn());
+ }
+
+ switch (keyBits) {
+ case IOConstantsV1.HDR_KEY_REF_U8:
+ out.writeByte(keyCode);
+ break;
+ case IOConstantsV1.HDR_KEY_REF_U16:
+ out.writeShort(keyCode);
+ break;
+ case IOConstantsV1.HDR_KEY_REF_S32:
+ out.writeInt(keyCode);
+ break;
+ case IOConstantsV1.HDR_KEY_DEF_QUAL:
+ writeString(keyword.prefix());
+ writeString(keyword.identifier());
+ keywords.put(keyword, keywords.size());
+ break;
+ case IOConstantsV1.HDR_KEY_DEF_UQUAL:
+ writeString(keyword.identifier());
+ keywords.put(keyword, keywords.size());
+ break;
+ default:
+ throw new IllegalStateException("Unhandled key bits " + keyBits);
+ }
+
+ if (argument != null) {
+ writeArgument(argument);
+ }
+
+ switch (sizeBits) {
+ case IOConstantsV1.HDR_SIZE_0:
+ // All done
+ return;
+ case IOConstantsV1.HDR_SIZE_U8:
+ out.writeByte(statements.size());
+ break;
+ case IOConstantsV1.HDR_SIZE_U16:
+ out.writeShort(size);
+ break;
+ case IOConstantsV1.HDR_SIZE_S32:
+ out.writeInt(size);
+ break;
+ default:
+ throw new IllegalStateException("Unhandled size bits " + sizeBits);
+ }
+
+ for (IRStatement child : statements) {
+ writeStatement(child);
+ }
+ }
+
+ private void writeString(final String str) throws IOException {
+ final Integer key = strings.get(str);
+ if (key != null) {
+ writeStringRef(key);
+ } else {
+ writeStringDef(0, str);
+ }
+ }
+
+ private void writeStringDef(final int bits, final String str) throws IOException {
+ strings.put(str, strings.size());
+
+ final int length = str.length();
+ if (length <= Short.MAX_VALUE / 2) {
+ out.writeByte(IOConstantsV1.STR_DEF_UTF | bits);
+ out.writeUTF(str);
+ } else if (length <= 1048576) {
+ final byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
+ if (bytes.length < 65536) {
+ out.writeByte(IOConstantsV1.STR_DEF_U16 | bits);
+ out.writeShort(bytes.length);
+ } else {
+ out.writeByte(IOConstantsV1.STR_DEF_S32 | bits);
+ out.writeInt(bytes.length);
+ }
+ out.write(bytes);
+ } else {
+ out.writeByte(IOConstantsV1.STR_DEF_CHARS | bits);
+ out.writeInt(length);
+ out.writeChars(str);
+ }
+ }
+
+ private void writeStringRef(final int strCode) throws IOException {
+ if (strCode <= 255) {
+ out.writeByte(IOConstantsV1.STR_REF_U8);
+ out.writeByte(strCode);
+ } else if (strCode <= 65535) {
+ out.writeByte(IOConstantsV1.STR_REF_U16);
+ out.writeShort(strCode);
+ } else {
+ out.writeByte(IOConstantsV1.STR_REF_S32);
+ out.writeInt(strCode);
+ }
+ }
+
+ private void writeHeader(final int keyBits, final int locationBits, final int sizeBits, final IRArgument argument)
+ throws IOException {
+ final int argBits = argument != null ? IOConstantsV1.HDR_ARGUMENT_PRESENT : IOConstantsV1.HDR_ARGUMENT_ABSENT;
+ out.writeByte(keyBits | sizeBits | argBits | locationBits);
+ }
+
+ private void writeArgument(final IRArgument argument) throws IOException {
+ if (argument instanceof Single) {
+ writeArgument((Single) argument);
+ } else if (argument instanceof Concatenation) {
+ writeArgument((Concatenation) argument);
+ } else {
+ throw new IllegalStateException("Unhandled argument " + argument);
+ }
+ }
+
+ private void writeArgument(final Single argument) throws IOException {
+ final int type;
+ if (argument.isValidIdentifier()) {
+ type = IOConstantsV1.ARG_TYPE_IDENTIFIER;
+ } else if (argument.needQuoteCheck()) {
+ type = IOConstantsV1.ARG_TYPE_UQUOT;
+ } else if (argument.needUnescape()) {
+ type = IOConstantsV1.ARG_TYPE_DQUOT;
+ } else {
+ type = IOConstantsV1.ARG_TYPE_SQUOT;
+ }
+
+ final String str = argument.string();
+ final Integer existing = strings.get(str);
+ if (existing != null) {
+ final int strCode = existing;
+ if (strCode <= 255) {
+ out.writeByte(type | IOConstantsV1.STR_REF_U8);
+ out.writeByte(strCode);
+ } else if (strCode <= 65535) {
+ out.writeByte(type | IOConstantsV1.STR_REF_U16);
+ out.writeShort(strCode);
+ } else {
+ out.writeByte(type | IOConstantsV1.STR_REF_S32);
+ out.writeInt(strCode);
+ }
+ } else {
+ writeStringDef(type, str);
+ }
+ }
+
+ private void writeArgument(final Concatenation argument) throws IOException {
+ final List<? extends Single> parts = argument.parts();
+ final int size = parts.size();
+ if (size <= 255) {
+ out.writeByte(IOConstantsV1.ARG_TYPE_CONCAT_U8);
+ out.writeByte(size);
+ } else if (size <= 65535) {
+ out.writeByte(IOConstantsV1.ARG_TYPE_CONCAT_U16);
+ out.writeShort(size);
+ } else {
+ out.writeByte(IOConstantsV1.ARG_TYPE_CONCAT_S32);
+ out.writeInt(size);
+ }
+
+ for (Single part : parts) {
+ writeArgument(part);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.antlr;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.opendaylight.yangtools.yang.ir.IOSupport;
+import org.opendaylight.yangtools.yang.ir.IRStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.YangIRSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+class IOSupportTest {
+ private static YangIRSchemaSource FOO;
+
+ @BeforeAll
+ static void beforeClass() throws Exception {
+ FOO = TextToIRTransformer.transformText(TestUtils.assertSchemaSource("/bugs/YT1089/foo.yang"));
+ }
+
+ @Test
+ void testSerializedSize() throws IOException {
+ final byte[] bytes = serialize(FOO.getRootStatement());
+ assertEquals(485, bytes.length);
+ }
+
+ @Test
+ void testSerdes() throws IOException {
+ final var orig = FOO.getRootStatement();
+ assertEquals(orig, deserialize(serialize(orig)));
+ }
+
+ private static byte[] serialize(final IRStatement stmt) throws IOException {
+ final var baos = new ByteArrayOutputStream();
+
+ try (var dos = new DataOutputStream(baos)) {
+ IOSupport.writeStatement(dos, stmt);
+ }
+
+ return baos.toByteArray();
+ }
+
+ private static IRStatement deserialize(final byte[] bytes) throws IOException {
+ try (var dis = new DataInputStream(new ByteArrayInputStream(bytes))) {
+ final var stmt = IOSupport.readStatement(dis);
+ assertEquals(0, dis.available());
+ return stmt;
+ }
+ }
+}
import java.io.File;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
final @Nullable Set<QName> supportedFeatures) throws Exception {
final var reactor = RFC7950Reactors.defaultReactor().newBuild();
for (var resourcePath : yangSourceFilePath) {
- reactor.addSource(YangStatementStreamSource.create(YangTextSchemaSource.forPath(Path.of(
- TestUtils.class.getResource(resourcePath).toURI()))));
+ reactor.addSource(YangStatementStreamSource.create(assertSchemaSource(resourcePath)));
}
if (supportedFeatures != null) {
reactor.setSupportedFeatures(FeatureSet.of(supportedFeatures));
return reactor.buildEffective();
}
+ public static YangTextSchemaSource assertSchemaSource(final String resourcePath) {
+ try {
+ return YangTextSchemaSource.forPath(Path.of(TestUtils.class.getResource(resourcePath).toURI()));
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e);
+ }
+ }
+
// FIXME: these remain unaudited
public static EffectiveModelContext loadYinModules(final URI resourceDirectory)