///|
fn validate_encoding(
encoding : TypeEncoding,
name : String,
) -> Unit raise SbeError {
if encoding.presence == Optional && encoding.length != 1 {
raise Unsupported("optional primitive arrays: \{name}")
}
if encoding.presence == Constant {
let text = encoding.constant.unwrap_or("")
if encoding.primitive == Char {
let bytes = encode_text(text, encoding.character_encoding, name)
if bytes.length() != encoding.length {
raise InvalidSchema(
"constant char length differs from its value: \{name}",
0,
)
}
} else {
if encoding.length != 1 {
raise Unsupported("constant numeric arrays")
}
let scalar = scalar_from_literal(encoding.primitive, text)
check_scalar(encoding, scalar, name)
}
}
if encoding.primitive == Char {
return
}
for value in [encoding.min_value, encoding.max_value, encoding.null_value] {
match value {
None => ()
Some(text) => {
let scalar = scalar_from_literal(encoding.primitive, text)
// Validate the physical integer width independently of the min/max
// constraints themselves. A fresh writer avoids incidental limits.
write_scalar(Writer::new(limit=8), encoding.primitive, scalar)
}
}
}
match (encoding.min_value, encoding.max_value) {
(Some(min), Some(max)) => {
let a = scalar_from_literal(encoding.primitive, min)
let b = scalar_from_literal(encoding.primitive, max)
let ordered = if encoding.primitive.is_signed() {
a.as_signed() <= b.as_signed()
} else if encoding.primitive.is_float() {
!a.as_float().is_nan() &&
!b.as_float().is_nan() &&
a.as_float() <= b.as_float()
} else {
a.as_unsigned() <= b.as_unsigned()
}
if !ordered {
raise InvalidSchema("minValue exceeds maxValue: \{name}", 0)
}
}
_ => ()
}
}
///|
fn validate_block(
schema : Schema,
block : BlockDef,
depth : Int32,
) -> Unit raise SbeError {
if depth > 32 {
raise LimitExceeded("group nesting exceeds 32")
}
let mut previous_since = 0
for field in block.fields {
if field.since_version > schema.version ||
field.since_version < previous_since {
raise InvalidSchema(
"fixed field sinceVersion must be nondecreasing and within schema version",
0,
)
}
previous_since = field.since_version
let definition = schema.type_def(field.type_name)
match definition {
Encoded(encoding) if encoding.length == 0 =>
raise InvalidSchema("zero-length varData type used as a fixed field", 0)
Composite(_, _) | BitSet(_, _) if field.presence != Required =>
raise Unsupported("optional or constant composite/set fields")
Encoded(encoding) if field.presence == Optional && encoding.length != 1 =>
raise Unsupported("optional primitive arrays")
_ => ()
}
if field.presence == Constant {
ignore(constant_value(schema, field))
}
if field.value_ref != None && field.presence != Constant {
raise InvalidSchema("valueRef is valid only for a constant field", 0)
}
}
for group in block.groups {
if group.since_version > schema.version || group.max_count < 0 {
raise InvalidSchema("group version or count bound is invalid", 0)
}
validate_block(schema, group.block, depth + 1)
}
for data in block.data {
if data.since_version > schema.version || data.max_length < 0 {
raise InvalidSchema("variable data version or length bound is invalid", 0)
}
}
}
///|
fn validate_compiled_schema(schema : Schema) -> Unit raise SbeError {
for name, definition in schema.types {
match definition {
Encoded(encoding) => validate_encoding(encoding, name)
Enumeration(primitive, cases, _) => {
let values : Array[Value] = []
for entry in cases {
if entry.since_version > schema.version {
raise InvalidSchema("enum case is newer than schema", 0)
}
let scalar = scalar_from_literal(primitive, entry.value)
check_scalar(default_encoding(primitive), scalar, name)
for previous in values {
if same_scalar(scalar, previous) {
raise InvalidSchema("duplicate numeric enum value", 0)
}
}
values.push(scalar)
}
}
BitSet(_, cases) =>
for entry in cases {
if entry.since_version > schema.version {
raise InvalidSchema("set choice is newer than schema", 0)
}
}
Composite(entries, _) =>
if entries.is_empty() {
raise InvalidSchema("composite must contain a type or reference", 0)
}
}
}
for message in schema.messages {
if message.since_version > schema.version {
raise InvalidSchema("message is newer than schema", 0)
}
validate_block(schema, message.block, 0)
}
}