///|
fn encoding_null(encoding : TypeEncoding) -> Value raise SbeError {
match encoding.null_value {
Some(text) => scalar_from_literal(encoding.primitive, text)
None => default_null(encoding.primitive)
}
}
///|
fn check_scalar(
encoding : TypeEncoding,
value : Value,
path : String,
) -> Unit raise SbeError {
let primitive = encoding.primitive
if primitive == Char {
let text = value.as_text()
let bytes = @utf8.encode(text)
if bytes.length() != 1 || bytes[0].to_int() < 32 || bytes[0].to_int() > 126 {
raise InvalidValue(path, "char must be a printable ASCII byte")
}
return
}
if primitive.is_signed() {
let number = value.as_signed()
let bits = primitive.width() * 8
let min = match encoding.min_value {
Some(text) => scalar_from_literal(primitive, text).as_signed()
None =>
if bits == 64 {
-9223372036854775807L
} else {
0L - (1L << (bits - 1)) + 1L
}
}
let max = match encoding.max_value {
Some(text) => scalar_from_literal(primitive, text).as_signed()
None =>
if bits == 64 {
9223372036854775807L
} else {
(1L << (bits - 1)) - 1L
}
}
if number < min ||
number > max ||
same_scalar(Signed(number), encoding_null(encoding)) {
raise InvalidValue(path, "signed integer is outside its SBE value range")
}
} else if primitive.is_float() {
let number = value.as_float()
if number.is_nan() || number.is_inf() {
raise InvalidValue(path, "required floating value must be finite")
}
if primitive == Float32 && Float::from_double(number).is_inf() {
raise InvalidValue(path, "value overflows float32")
}
match encoding.min_value {
Some(text) if number < scalar_from_literal(primitive, text).as_float() =>
raise InvalidValue(path, "float is below minValue")
_ => ()
}
match encoding.max_value {
Some(text) if number > scalar_from_literal(primitive, text).as_float() =>
raise InvalidValue(path, "float exceeds maxValue")
_ => ()
}
} else {
let number = value.as_unsigned()
let min = match encoding.min_value {
Some(text) => scalar_from_literal(primitive, text).as_unsigned()
None => 0UL
}
let max = match encoding.max_value {
Some(text) => scalar_from_literal(primitive, text).as_unsigned()
None =>
if primitive.width() == 8 {
0xfffffffffffffffeUL
} else {
(1UL << (primitive.width() * 8)) - 2UL
}
}
if number < min ||
number > max ||
same_scalar(Unsigned(number), encoding_null(encoding)) {
raise InvalidValue(
path, "unsigned integer is outside its SBE value range",
)
}
}
}
///|
fn write_scalar(
writer : Writer,
primitive : Primitive,
value : Value,
) -> Unit raise SbeError {
if primitive == Char {
writer.write_bytes(@utf8.encode(value.as_text()))
} else if primitive.is_float() {
writer.write_float(value.as_float(), primitive.width())
} else if primitive.is_signed() {
writer.write_int(value.as_signed(), primitive.width())
} else {
writer.write_uint(value.as_unsigned(), primitive.width())
}
}
///|
fn read_scalar(reader : Reader, primitive : Primitive) -> Value raise SbeError {
if primitive == Char {
let code = reader.read_uint(1).to_int()
match code.to_char() {
Some(char) => Text(char.to_string())
None => raise InvalidMessage("invalid character byte")
}
} else if primitive.is_float() {
Floating(reader.read_float(primitive.width()))
} else if primitive.is_signed() {
Signed(reader.read_int(primitive.width()))
} else {
Unsigned(reader.read_uint(primitive.width()))
}
}
///|
fn encode_text(
text : String,
character_encoding : String,
path : String,
) -> Bytes raise SbeError {
let bytes = @utf8.encode(text)
if character_encoding == "ASCII" || character_encoding == "US-ASCII" {
for byte in bytes {
if byte.to_int() > 127 {
raise InvalidValue(path, "non-ASCII character in ASCII field")
}
}
}
bytes
}
///|
fn decode_text(
bytes : Bytes,
character_encoding : String,
) -> String raise SbeError {
if character_encoding == "ASCII" || character_encoding == "US-ASCII" {
for byte in bytes {
if byte.to_int() > 127 {
raise InvalidMessage("non-ASCII byte in ASCII field")
}
}
}
@utf8.decode(bytes) catch {
_ => raise InvalidMessage("malformed UTF-8 in text field")
}
}
///|
fn constant_value(schema : Schema, field : FieldDef) -> Value raise SbeError {
match field.value_ref {
Some(reference) => {
let parts = reference.split(".").to_array()
if parts.length() != 2 {
raise InvalidSchema("valueRef must be Enum.Value", 0)
}
let enum_name = parts[0].to_owned()
let case_name = parts[1].to_owned()
match schema.type_def(enum_name) {
Enumeration(primitive, cases, _) => {
for entry in cases {
if entry.name == case_name {
return match schema.type_def(field.type_name) {
Enumeration(_, _, _) => Text(case_name)
_ => scalar_from_literal(primitive, entry.value)
}
}
}
raise InvalidSchema("unknown valueRef case: \{reference}", 0)
}
_ => raise InvalidSchema("valueRef target must be an enum", 0)
}
}
None =>
match schema.type_def(field.type_name) {
Encoded(encoding) =>
match encoding.constant {
Some(text) =>
if encoding.primitive == Char {
Text(text)
} else {
scalar_from_literal(encoding.primitive, text)
}
None => raise InvalidSchema("constant field has no value", 0)
}
_ => raise InvalidSchema("constant field has no valueRef", 0)
}
}
}