serializeParams function
Serializes a list of parameter values to binary format.
The params list should contain ParamValue instances in the order
they appear in the prepared statement.
Returns a Uint8List containing the serialized parameters.
Uses a two-pass strategy: phase 1 sizes the buffer (ASCII text writes code units directly in phase 2; non-ASCII is UTF-8 encoded once); phase 2 writes into a single pre-sized Uint8List.
Implementation
Uint8List serializeParams(List<ParamValue> params) {
if (params.isEmpty) return Uint8List(0);
// Phase 1: size payloads. `encodedText[i] == null` means ASCII (or unused);
// non-null holds a one-shot UTF-8 encoding for non-ASCII text.
var totalBytes = 0;
final encodedText = List<List<int>?>.filled(params.length, null);
for (var i = 0; i < params.length; i++) {
switch (params[i]) {
case ParamValueNull():
totalBytes += 5;
case ParamValueString(:final value):
if (_isAsciiOnly(value)) {
totalBytes += 5 + value.length;
} else {
final b = utf8.encode(value);
encodedText[i] = b;
totalBytes += 5 + b.length;
}
case ParamValueInt32():
totalBytes += 9;
case ParamValueInt64():
totalBytes += 13;
case ParamValueDecimal(:final value):
if (_isAsciiOnly(value)) {
totalBytes += 5 + value.length;
} else {
final b = utf8.encode(value);
encodedText[i] = b;
totalBytes += 5 + b.length;
}
case ParamValueBinary(:final value):
totalBytes += 5 + value.length;
case ParamValueRefCursorOut():
totalBytes += 5;
}
}
// Phase 2: write all params into the pre-sized buffer.
final out = Uint8List(totalBytes);
final bd = ByteData.sublistView(out);
var off = 0;
for (var i = 0; i < params.length; i++) {
switch (params[i]) {
case ParamValueNull():
out[off] = _tagNull;
bd.setUint32(off + 1, 0, _littleEndian);
off += 5;
case ParamValueString(:final value):
final encoded = encodedText[i];
if (encoded != null) {
out[off] = _tagString;
bd.setUint32(off + 1, encoded.length, _littleEndian);
out.setRange(off + 5, off + 5 + encoded.length, encoded);
off += 5 + encoded.length;
} else {
out[off] = _tagString;
bd.setUint32(off + 1, value.length, _littleEndian);
_writeAsciiString(out, off + 5, value);
off += 5 + value.length;
}
case ParamValueInt32(:final value):
out[off] = _tagInteger;
bd.setUint32(off + 1, 4, _littleEndian);
bd.setInt32(off + 5, value, _littleEndian);
off += 9;
case ParamValueInt64(:final value):
out[off] = _tagBigInt;
bd.setUint32(off + 1, 8, _littleEndian);
bd.setInt64(off + 5, value, _littleEndian);
off += 13;
case ParamValueDecimal(:final value):
final encoded = encodedText[i];
if (encoded != null) {
out[off] = _tagDecimal;
bd.setUint32(off + 1, encoded.length, _littleEndian);
out.setRange(off + 5, off + 5 + encoded.length, encoded);
off += 5 + encoded.length;
} else {
out[off] = _tagDecimal;
bd.setUint32(off + 1, value.length, _littleEndian);
_writeAsciiString(out, off + 5, value);
off += 5 + value.length;
}
case ParamValueBinary(:final value):
final vLen = value.length;
out[off] = _tagBinary;
bd.setUint32(off + 1, vLen, _littleEndian);
out.setRange(off + 5, off + 5 + vLen, value);
off += 5 + vLen;
case ParamValueRefCursorOut():
out[off] = _tagRefCursorOut;
bd.setUint32(off + 1, 0, _littleEndian);
off += 5;
}
}
return out;
}