22// SPDX-License-Identifier: Apache-2.0
33package com.amazon.ion.bytecode
44
5+ import com.amazon.ion.IonException
6+ import com.amazon.ion.IonType
7+ import com.amazon.ion.SystemSymbols
58import com.amazon.ion.bytecode.util.BytecodeBuffer
69import com.amazon.ion.bytecode.util.ConstantPool
10+ import com.amazon.ion.bytecode.util.StringPool
11+ import com.amazon.ion.impl.ArrayBackedLstSnapshot
712import com.amazon.ion.ion_1_1.MacroImpl
813import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
914
1015/* *
1116 * TODO:
1217 * Write more documentation.
13- * Implement stubbed out methods.
18+ * Implement remaining stubbed out methods.
19+ * Do we need some way to "garbage collect" from the constant pool?
1420 *
1521 * Notes:
1622 *
23+ * Terminology:
24+ * - "effective" symbol/macro table -- the tables that are currently in use and exposed to the reader
25+ * - "spare" symbol/macro table -- essentially we have an object pool with size 1, allowing us to modify the inactive
26+ * tables and then swap them for the active tables once the changes are complete.
27+ *
1728 * It is never safe to remove or modify any existing data in the effective tables. It is safe to append data to those
1829 * tables for an `add_symbols`, `add_macros`, or `use` directive (as long as the active encoding modules are just `$ion` and `_`).
1930 */
@@ -34,14 +45,21 @@ internal class EncodingContextManager {
3445 )
3546 }
3647
37- // These make up the effective macro table and effective symbol table
38- private var macroBytecode = BytecodeBuffer ()
39- private var macroOffsets = BytecodeBuffer ()
40- private var macroNames = ConstantPool ()
41- private var symbols = mutableListOf<String ?>().apply { SYSTEM_SYMBOLS .forEach { add(it) } }
42-
43- // TODO: Do we need the constant pool here?
44- private var constants = ConstantPool ()
48+ class TableSet {
49+ @JvmField var macroBytecode = BytecodeBuffer ()
50+ @JvmField var macroOffsets = BytecodeBuffer ()
51+ @JvmField var macroNames = ConstantPool ()
52+ @JvmField var symbols = StringPool ().apply { SYSTEM_SYMBOLS .forEach { add(it) } }
53+ @JvmField var constants = ConstantPool ()
54+
55+ fun reset () {
56+ macroBytecode.clear()
57+ macroOffsets.clear()
58+ macroNames.clear()
59+ symbols.truncate(SystemSymbols .ION_1_0_MAX_ID + 1 )
60+ constants.clear()
61+ }
62+ }
4563
4664 private class Module (
4765 val symbols : Array <String >,
@@ -54,34 +72,59 @@ internal class EncodingContextManager {
5472 // Tracks only modules _other_ than the system module and default module
5573 private var additionalActiveModules = mutableListOf<Module >()
5674
57- @SuppressFBWarnings(" IE_EXPOSE_REP" , justification = " array is accessible for performance" )
58- fun getEffectiveMacroTableBytecode (): IntArray = macroBytecode.unsafeGetArray()
75+ private val effectiveTables = TableSet ()
76+ // TODO(simplification): we might not need the spare tables for macros because
77+ // macros are already evaluated before we get to this point.
78+ private val spareTables = TableSet ()
5979
6080 @SuppressFBWarnings(" IE_EXPOSE_REP" , justification = " array is accessible for performance" )
61- fun getEffectiveMacroTableOffsets (): IntArray = macroOffsets.unsafeGetArray()
62-
63- fun getEffectiveSymbolTable (): Array <String ?> = symbols.toTypedArray()
64-
81+ fun getEffectiveMacroTableBytecode (): IntArray = effectiveTables.macroBytecode.unsafeGetArray()
82+ @SuppressFBWarnings(" IE_EXPOSE_REP" , justification = " array is accessible for performance" )
83+ fun getEffectiveMacroTableOffsets (): IntArray = effectiveTables.macroOffsets.unsafeGetArray()
84+ @SuppressFBWarnings(" IE_EXPOSE_REP" , justification = " array is accessible for performance" )
85+ fun getEffectiveSymbolTable (): Array <String ?> = effectiveTables.symbols.unsafeGetArray()
6586 @SuppressFBWarnings(" IE_EXPOSE_REP" , justification = " array is accessible for performance" )
66- fun getEffectiveConstantPool (): Array <Any ?> = constants.unsafeGetArray()
87+ fun getEffectiveConstantPool (): Array <Any ?> = effectiveTables.constants.unsafeGetArray()
88+
89+ fun getLstSnapshot () = ArrayBackedLstSnapshot (effectiveTables.symbols)
6790
6891 /* * Called when encountering an IVM */
6992 fun reset () {
7093 additionalActiveModules.clear()
7194 additionalAvailableModules.clear()
72- macroBytecode.clear()
73- macroOffsets.clear()
74- macroNames.clear()
75- symbols.clear()
76- SYSTEM_SYMBOLS .forEach { symbols.add(it) }
77- constants.clear()
95+ effectiveTables.reset()
7896 }
7997
8098 /* *
8199 * The [BytecodeIonReader] should be positioned in the directive, but not on the first value yet.
82100 * When this method returns, the [BytecodeIonReader] will be positioned at the end of the directive, but not stepped out.
83101 */
84102 fun readSetSymbolsDirective (reader : BytecodeIonReader ) {
103+ if (additionalActiveModules.isNotEmpty()) {
104+ readSetSymbolsWithActiveModules(reader)
105+ return
106+ }
107+
108+ val symbols = spareTables.symbols
109+ symbols.truncate(SystemSymbols .ION_1_0_MAX_ID + 1 )
110+ while (true ) {
111+ val s = when (reader.next()) {
112+ IonType .SYMBOL ,
113+ IonType .STRING -> reader.stringValue()
114+ null -> break
115+ else -> throw IonException (" Expected text; found ${reader.type} " )
116+ }
117+ symbols.add(s)
118+ }
119+
120+ // Swap the effective and spare symbol tables
121+ spareTables.symbols = effectiveTables.symbols
122+ effectiveTables.symbols = symbols
123+ }
124+
125+ private fun readSetSymbolsWithActiveModules (reader : BytecodeIonReader ) {
126+ // Update the default module then call:
127+ // rebuildEffectiveSymbolTable(updateReaderSymbolTable)
85128 TODO ()
86129 }
87130
@@ -90,6 +133,26 @@ internal class EncodingContextManager {
90133 * When this method returns, the [BytecodeIonReader] will be positioned at the end of the directive, but not stepped out.
91134 */
92135 fun readAddSymbols (reader : BytecodeIonReader ) {
136+ if (additionalActiveModules.isNotEmpty()) {
137+ readAddSymbolsWithActiveModules(reader)
138+ return
139+ }
140+
141+ val symbols = effectiveTables.symbols
142+ while (true ) {
143+ val s = when (reader.next()) {
144+ IonType .SYMBOL ,
145+ IonType .STRING -> reader.stringValue()
146+ null -> break
147+ else -> throw IonException (" Expected text; found ${reader.type} " )
148+ }
149+ symbols.add(s)
150+ }
151+ }
152+
153+ private fun readAddSymbolsWithActiveModules (reader : BytecodeIonReader ) {
154+ // Update the default module then call:
155+ // rebuildEffectiveSymbolTable(updateReaderSymbolTable)
93156 TODO ()
94157 }
95158
@@ -142,4 +205,21 @@ internal class EncodingContextManager {
142205 fun readEncodingDirective (reader : BytecodeIonReader ) {
143206 TODO ()
144207 }
208+
209+ /* *
210+ * Rebuilds the effective symbol table using all the active encoding modules
211+ */
212+ private fun rebuildEffectiveSymbolTable () {
213+ val newEffectiveSymbolTable = spareTables.symbols
214+ newEffectiveSymbolTable.truncate(SystemSymbols .ION_1_0_MAX_ID + 1 )
215+ // TODO: Make this more efficient with an array copy operation.
216+ additionalActiveModules.forEach { m -> m.symbols.forEach { newEffectiveSymbolTable.add(it) } }
217+
218+ spareTables.symbols = effectiveTables.symbols
219+ effectiveTables.symbols = newEffectiveSymbolTable
220+ }
221+
222+ private fun rebuildEffectiveMacroTable () {
223+ TODO ()
224+ }
145225}
0 commit comments