Skip to content

Commit 439daff

Browse files
committed
add method to encode tuple element
1 parent 721532f commit 439daff

File tree

1 file changed

+100
-1
lines changed

1 file changed

+100
-1
lines changed

pyteal/ast/abi/tuple.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pyteal.ast.int import Int
2222
from pyteal.ast.bytes import Bytes
2323
from pyteal.ast.unaryexpr import Len
24-
from pyteal.ast.binaryexpr import ExtractUint16
24+
from pyteal.ast.binaryexpr import ExtractUint16, GetBit
2525
from pyteal.ast.naryexpr import Concat
2626
from pyteal.ast.abstractvar import alloc_abstract_var
2727

@@ -117,6 +117,98 @@ def _encode_tuple(values: Sequence[BaseType]) -> Expr:
117117
return Concat(*toConcat)
118118

119119

120+
121+
def _index_tuple_bytes(
122+
value_types: Sequence[TypeSpec], encoded: Expr, index: int
123+
) -> Expr:
124+
if not (0 <= index < len(value_types)):
125+
raise ValueError("Index outside of range")
126+
127+
offset = 0
128+
ignoreNext = 0
129+
lastBoolStart = 0
130+
lastBoolLength = 0
131+
for i, typeBefore in enumerate(value_types[:index]):
132+
if ignoreNext > 0:
133+
ignoreNext -= 1
134+
continue
135+
136+
if typeBefore == BoolTypeSpec():
137+
lastBoolStart = offset
138+
lastBoolLength = _consecutive_bool_type_spec_num(value_types, i)
139+
offset += _bool_sequence_length(lastBoolLength)
140+
ignoreNext = lastBoolLength - 1
141+
continue
142+
143+
if typeBefore.is_dynamic():
144+
offset += 2
145+
continue
146+
147+
offset += typeBefore.byte_length_static()
148+
149+
valueType = value_types[index]
150+
151+
if type(valueType) is Bool:
152+
if ignoreNext > 0:
153+
# value is in the middle of a bool sequence
154+
bitOffsetInBoolSeq = lastBoolLength - ignoreNext
155+
bitOffsetInEncoded = lastBoolStart * NUM_BITS_IN_BYTE + bitOffsetInBoolSeq
156+
else:
157+
# value is the beginning of a bool sequence (or a single bool)
158+
bitOffsetInEncoded = offset * NUM_BITS_IN_BYTE
159+
return GetBit(encoded, Int(bitOffsetInEncoded))
160+
161+
if valueType.is_dynamic():
162+
hasNextDynamicValue = False
163+
nextDynamicValueOffset = offset + 2
164+
ignoreNext = 0
165+
for i, typeAfter in enumerate(value_types[index + 1 :], start=index + 1):
166+
if ignoreNext > 0:
167+
ignoreNext -= 1
168+
continue
169+
170+
if type(typeAfter) is BoolTypeSpec:
171+
boolLength = _consecutive_bool_type_spec_num(value_types, i)
172+
nextDynamicValueOffset += _bool_sequence_length(boolLength)
173+
ignoreNext = boolLength - 1
174+
continue
175+
176+
if typeAfter.is_dynamic():
177+
hasNextDynamicValue = True
178+
break
179+
180+
nextDynamicValueOffset += typeAfter.byte_length_static()
181+
182+
start_index = ExtractUint16(encoded, Int(offset))
183+
if not hasNextDynamicValue:
184+
# This is the final dynamic value, so decode the substring from start_index to the end of
185+
# encoded
186+
return substring_for_decoding(encoded, start_index=start_index)
187+
188+
# There is a dynamic value after this one, and end_index is where its tail starts, so decode
189+
# the substring from start_index to end_index
190+
end_index = ExtractUint16(encoded, Int(nextDynamicValueOffset))
191+
return substring_for_decoding(encoded, start_index=start_index, end_index=end_index)
192+
193+
start_index = Int(offset)
194+
length = Int(valueType.byte_length_static())
195+
196+
if index + 1 == len(value_types):
197+
if offset == 0:
198+
# This is the first and only value in the tuple, so decode all of encoded
199+
return encoded
200+
# This is the last value in the tuple, so decode the substring from start_index to the end of
201+
# encoded
202+
return substring_for_decoding(encoded, start_index=start_index)
203+
204+
if offset == 0:
205+
# This is the first value in the tuple, so decode the substring from 0 with length length
206+
return substring_for_decoding(encoded, length=length)
207+
208+
# This is not the first or last value, so decode the substring from start_index with length length
209+
return substring_for_decoding(encoded, start_index=start_index, length=length)
210+
211+
120212
def _index_tuple(
121213
value_types: Sequence[TypeSpec], encoded: Expr, index: int, output: BaseType
122214
) -> Expr:
@@ -405,6 +497,13 @@ def store_into(self, output: T) -> Expr:
405497
output,
406498
)
407499

500+
def encode(self)->Expr:
501+
return _index_tuple_bytes(
502+
self.tuple.type_spec().value_type_specs(),
503+
self.tuple.encode(),
504+
self.index,
505+
)
506+
408507

409508
TupleElement.__module__ = "pyteal.abi"
410509

0 commit comments

Comments
 (0)