///|
/// SQLite storage classes. Integers are never converted through floating point.
pub(all) enum Value {
Null
Integer(Int64)
Real(Double)
Text(String)
Blob(Bytes)
} derive(Debug, Eq)
///|
pub suberror CdcError {
Invalid(String)
Conflict(String)
Stale(String)
Corrupt(String)
} derive(Debug)
///|
pub fn Value::storage_class(self : Value) -> String {
match self {
Null => "null"
Integer(_) => "integer"
Real(_) => "real"
Text(_) => "text"
Blob(_) => "blob"
}
}
///|
pub fn Value::validate(self : Value) -> Unit raise CdcError {
if self is Real(v) && (v - v).is_nan() {
raise Invalid("Non-finite REAL is not supported by the wire protocol")
}
}
///|
pub fn encode_hex(bytes : Bytes) -> String {
let out = StringBuilder()
let digits = "0123456789abcdef".to_array()
for b in bytes {
let n = b.to_int()
out.write_char(digits[n / 16])
out.write_char(digits[n % 16])
}
out.to_string()
}
///|
fn hex_digit(c : UInt16) -> Int32 raise CdcError {
let n = c.to_int()
if n >= 48 && n <= 57 {
n - 48
} else if n >= 65 && n <= 70 {
n - 55
} else if n >= 97 && n <= 102 {
n - 87
} else {
raise Invalid("Invalid hexadecimal digit")
}
}
///|
pub fn decode_hex(text : String) -> Bytes raise CdcError {
if text.length() % 2 != 0 {
raise Invalid("Hexadecimal BLOB must have even length")
}
let values : Array[Byte] = []
for i in 0..<(text.length() / 2) {
values.push(
(hex_digit(text[i * 2]) * 16 + hex_digit(text[i * 2 + 1])).to_byte(),
)
}
Bytes::from_iter(values.iter())
}
///|
pub fn parse_integer(text : String) -> Int64 raise CdcError {
if text.is_empty() {
raise Invalid("Empty integer")
}
let negative = text.has_prefix("-")
let offset = if negative { 1 } else { 0 }
if text.length() == offset {
raise Invalid("Integer has no digits")
}
let limit = if negative {
-9223372036854775807L - 1L
} else {
-9223372036854775807L
}
let mut result = 0L
for i in offset.. 9 {
raise Invalid("Invalid decimal integer: " + text)
}
if result < limit / 10L {
raise Invalid("Integer overflow")
}
result *= 10L
if result < limit + digit.to_int64() {
raise Invalid("Integer overflow")
}
result -= digit.to_int64()
}
if negative {
result
} else {
-result
}
}
///|
pub fn Value::to_wire(self : Value) -> Json raise CdcError {
self.validate()
match self {
Null => Json::array([Json::string("null")])
Integer(v) =>
Json::array([Json::string("integer"), Json::string(v.to_string())])
Real(v) => Json::array([Json::string("real"), Json::string(v.to_string())])
Text(v) => Json::array([Json::string("text"), Json::string(v)])
Blob(v) => Json::array([Json::string("blob"), Json::string(encode_hex(v))])
}
}
///|
pub fn Value::from_wire(value : Json) -> Value raise CdcError {
let result = match value {
["null"] => Null
["integer", String(v)] => Integer(parse_integer(v))
["real", String(v)] =>
match parse_json(v) {
Number(number, ..) => Real(number)
_ => raise Invalid("Invalid REAL decimal")
}
["text", String(v)] => Text(v)
["blob", String(v)] => Blob(decode_hex(v))
_ => raise Invalid("Expected tagged SQLite value")
}
result.validate()
result
}
///|
pub fn parse_json(text : String) -> Json raise CdcError {
@json.parse(text) catch {
_ => raise Invalid("Invalid JSON document")
}
}
///|
pub fn field(value : Json, name : String) -> Json raise CdcError {
match value {
Object(map) =>
match map.get(name) {
Some(v) => v
None => raise Invalid("Missing field: " + name)
}
_ => raise Invalid("Expected JSON object")
}
}
///|
pub fn string_field(value : Json, name : String) -> String raise CdcError {
match field(value, name) {
String(text) => text
_ => raise Invalid("Expected string field: " + name)
}
}
///|
pub fn integer_field(value : Json, name : String) -> Int64 raise CdcError {
parse_integer(string_field(value, name))
}
///|
pub fn array_field(value : Json, name : String) -> Array[Json] raise CdcError {
match field(value, name) {
Array(values) => values
_ => raise Invalid("Expected array field: " + name)
}
}
///|
pub fn boolean_field(value : Json, name : String) -> Bool raise CdcError {
match field(value, name) {
True => true
False => false
_ => raise Invalid("Expected boolean field: " + name)
}
}