@@ -52,69 +52,77 @@ public Array decode(DecodeContext ctx) {
5252 throw new VortexException (EncodingId .VORTEX_CONSTANT , "invalid scalar value" , e );
5353 }
5454
55- long n = ctx .rowCount ();
55+ return arrayFromScalar (ctx , scalar , ctx .dtype (), ctx .rowCount ());
56+ }
5657
57- if (ctx .dtype () instanceof DType .Null ) {
58- return new NullArray (ctx .dtype (), n );
58+ /// Builds the constant array for `scalar` interpreted as `dtype`, broadcast to `n` rows.
59+ /// Recurses for Extension (its storage dtype) and Variant (the wrapped inner scalar).
60+ private static Array arrayFromScalar (DecodeContext ctx , ScalarValue scalar , DType dtype , long n ) {
61+ if (dtype instanceof DType .Null ) {
62+ return new NullArray (dtype , n );
5963 }
60-
61- if (ctx .dtype () instanceof DType .Utf8 || ctx .dtype () instanceof DType .Binary ) {
62- return decodeString (ctx , scalar , n );
64+ if (dtype instanceof DType .Variant ) {
65+ // A constant variant wraps a typed inner scalar (Scalar::variant(inner)); the
66+ // physical storage is the inner-typed constant array. The VariantArray wrapper
67+ // re-applies the logical Variant dtype.
68+ io .github .dfa1 .vortex .proto .Scalar inner = scalar .variant_value ();
69+ if (inner == null || inner .value () == null ) {
70+ throw new VortexException (EncodingId .VORTEX_CONSTANT , "constant variant missing variant_value" );
71+ }
72+ DType innerDtype = VariantEncodingDecoder .dtypeFromProto (inner .dtype ());
73+ return arrayFromScalar (ctx , inner .value (), innerDtype , n );
6374 }
64-
65- if (ctx .dtype () instanceof DType .Bool ) {
66- return decodeBool (ctx , scalar , n );
75+ if (dtype instanceof DType .Utf8 || dtype instanceof DType .Binary ) {
76+ return decodeString (ctx , scalar , dtype , n );
6777 }
68-
69- if (ctx .dtype () instanceof DType .Decimal ) {
70- return decodeDecimal (ctx , scalar , n );
78+ if (dtype instanceof DType .Bool ) {
79+ return decodeBool (dtype , scalar , n );
7180 }
72-
73- if (ctx .dtype () instanceof DType .Extension ext ) {
74- var storageCtx = new DecodeContext (ctx .node (), ext .storageDType (), ctx .rowCount (),
75- ctx .segmentBuffers (), ctx .registry (), ctx .arena ());
76- Array storage = decode (storageCtx );
77- // GenericArray needs a backing buffer; the recursive call now returns a
78- // metadata-only LazyConstantXxxArray. Materialise once into the chunk arena
79- // so downstream extension consumers that read via ArraySegments.of(arr) still
80- // find a segment. Extension-on-constant is rare enough that the small alloc
81- // doesn't matter — the bare primitive path stays buffer-free.
82- return new GenericArray (ctx .dtype (), n , ArraySegments .of (storage , ctx .arena ()));
81+ if (dtype instanceof DType .Decimal ) {
82+ return decodeDecimal (dtype , scalar , n );
8383 }
84-
85- if (!(ctx .dtype () instanceof DType .Primitive p )) {
86- throw new VortexException (EncodingId .VORTEX_CONSTANT , "unsupported dtype " + ctx .dtype ());
84+ if (dtype instanceof DType .Extension ext ) {
85+ Array storage = arrayFromScalar (ctx , scalar , ext .storageDType (), n );
86+ // GenericArray needs a backing buffer; the recursive call returns a metadata-only
87+ // LazyConstantXxxArray. Materialise once into the chunk arena so downstream
88+ // extension consumers that read via ArraySegments.of(arr) still find a segment.
89+ // Extension-on-constant is rare enough that the small alloc doesn't matter — the
90+ // bare primitive path stays buffer-free.
91+ return new GenericArray (dtype , n , ArraySegments .of (storage , ctx .arena ()));
92+ }
93+ if (!(dtype instanceof DType .Primitive p )) {
94+ throw new VortexException (EncodingId .VORTEX_CONSTANT , "unsupported dtype " + dtype );
8795 }
8896
8997 PType ptype = p .ptype ();
9098 long rawBits = scalarToRawBits (scalar , ptype );
9199
92100 return switch (ptype ) {
93- case I64 , U64 -> new LazyConstantLongArray (ctx . dtype () , n , rawBits );
94- case I32 , U32 -> new LazyConstantIntArray (ctx . dtype () , n , (int ) rawBits );
95- case F64 -> new LazyConstantDoubleArray (ctx . dtype () , n , Double .longBitsToDouble (rawBits ));
96- case F32 -> new LazyConstantFloatArray (ctx . dtype () , n , Float .intBitsToFloat ((int ) rawBits ));
97- case I16 , U16 -> new LazyConstantShortArray (ctx . dtype () , n , (short ) rawBits );
98- case I8 , U8 -> new LazyConstantByteArray (ctx . dtype () , n , (byte ) rawBits );
101+ case I64 , U64 -> new LazyConstantLongArray (dtype , n , rawBits );
102+ case I32 , U32 -> new LazyConstantIntArray (dtype , n , (int ) rawBits );
103+ case F64 -> new LazyConstantDoubleArray (dtype , n , Double .longBitsToDouble (rawBits ));
104+ case F32 -> new LazyConstantFloatArray (dtype , n , Float .intBitsToFloat ((int ) rawBits ));
105+ case I16 , U16 -> new LazyConstantShortArray (dtype , n , (short ) rawBits );
106+ case I8 , U8 -> new LazyConstantByteArray (dtype , n , (byte ) rawBits );
99107 default -> throw new VortexException (EncodingId .VORTEX_CONSTANT , "unsupported ptype " + ptype );
100108 };
101109 }
102110
103- private static Array decodeDecimal (DecodeContext ctx , ScalarValue scalar , long n ) {
111+ private static Array decodeDecimal (DType dtype , ScalarValue scalar , long n ) {
104112 byte [] elemBytes = scalar .bytes_value ();
105113 int elemLen = elemBytes .length ;
106114 // Decode the single scalar value via LazyDecimalArray (reuses its LE byte-order logic),
107115 // then wrap in a constant array — O(1) allocation regardless of row count.
108- var value = new LazyDecimalArray (ctx . dtype () , 1 , MemorySegment .ofArray (elemBytes ), elemLen ).getDecimal (0 );
109- return new LazyConstantDecimalArray (ctx . dtype () , n , value , elemLen );
116+ var value = new LazyDecimalArray (dtype , 1 , MemorySegment .ofArray (elemBytes ), elemLen ).getDecimal (0 );
117+ return new LazyConstantDecimalArray (dtype , n , value , elemLen );
110118 }
111119
112- private static Array decodeBool (DecodeContext ctx , ScalarValue scalar , long n ) {
120+ private static Array decodeBool (DType dtype , ScalarValue scalar , long n ) {
113121 boolean value = scalar .bool_value () != null && scalar .bool_value ();
114- return new LazyConstantBoolArray (ctx . dtype () , n , value );
122+ return new LazyConstantBoolArray (dtype , n , value );
115123 }
116124
117- private static Array decodeString (DecodeContext ctx , ScalarValue scalar , long n ) {
125+ private static Array decodeString (DecodeContext ctx , ScalarValue scalar , DType dtype , long n ) {
118126 byte [] strBytes = scalar .string_value () != null
119127 ? scalar .string_value ().getBytes (StandardCharsets .UTF_8 )
120128 : (scalar .bytes_value () != null ? scalar .bytes_value () : new byte [0 ]);
@@ -131,7 +139,7 @@ private static Array decodeString(DecodeContext ctx, ScalarValue scalar, long n)
131139 offsetsSeg .setAtIndex (PTypeIO .LE_INT , i , (int ) (i * strLen ));
132140 }
133141
134- return new VarBinArray .OffsetMode (ctx . dtype () , n , bytesSeg .asReadOnly (), offsetsSeg .asReadOnly (), PType .I32 );
142+ return new VarBinArray .OffsetMode (dtype , n , bytesSeg .asReadOnly (), offsetsSeg .asReadOnly (), PType .I32 );
135143 }
136144
137145 private static long scalarToRawBits (ScalarValue scalar , PType ptype ) {
0 commit comments