///|
fn object(value : Json) -> Map[String, Json] raise CodeError {
match value {
Object(fields) => fields
_ => raise InvalidFormat(0, "expected JSON object")
}
}
///|
fn allowed(
fields : Map[String, Json],
names : Array[String],
) -> Unit raise CodeError {
for key, _ in fields {
if !names.contains(key) {
raise InvalidFormat(0, "unknown field: " + key)
}
}
}
///|
fn required(fields : Map[String, Json], key : String) -> Json raise CodeError {
match fields.get(key) {
Some(value) => value
None => raise InvalidFormat(0, "missing field: " + key)
}
}
///|
fn json_integer(value : Json) -> Int32 raise CodeError {
match value {
Number(x, ..) => {
if !finite(x) || x < -1000000.0 || x > 1000000.0 || @math.floor(x) != x {
raise InvalidFormat(0, "integer outside [-1000000,1000000]")
}
x.to_int()
}
_ => raise InvalidFormat(0, "expected integer")
}
}
///|
fn json_string(value : Json) -> String raise CodeError {
match value {
String(text) => text
_ => raise InvalidFormat(0, "expected string")
}
}
///|
fn json_array(value : Json) -> Array[Json] raise CodeError {
match value {
Array(values) => {
if values.length() > 1000000 {
raise ResourceLimit("JSON array")
}
values
}
_ => raise InvalidFormat(0, "expected array")
}
}
///|
fn json_ints(value : Json) -> Array[Int32] raise CodeError {
let result : Array[Int32] = []
for item in json_array(value) {
result.push(json_integer(item))
}
result
}
///|
fn json_rows(value : Json) -> Array[Array[Int32]] raise CodeError {
let result : Array[Array[Int32]] = []
let mut count = 0
for row in json_array(value) {
let values = json_ints(row)
if values.length() > 1000000 - count {
raise ResourceLimit("JSON matrix cells")
}
count += values.length()
result.push(values)
}
result
}
///|
fn json_double(value : Json) -> Double raise CodeError {
match value {
Number(x, ..) =>
if finite(x) {
x
} else {
raise InvalidNumber("finite JSON number required")
}
_ => raise InvalidFormat(0, "expected number")
}
}
///|
fn json_doubles(value : Json) -> Array[Double] raise CodeError {
let result : Array[Double] = []
for item in json_array(value) {
result.push(json_double(item))
}
result
}
///|
fn code_from_json(value : Json) -> ParityCheck raise CodeError {
let fields = object(value)
if fields.contains("alist") {
allowed(fields, ["alist"])
return parse_alist(json_string(required(fields, "alist")))
}
if fields.contains("base") {
allowed(fields, ["base", "lift"])
return lift_qc(
json_rows(required(fields, "base")),
json_integer(required(fields, "lift")),
)
}
allowed(fields, ["variables", "rows"])
ParityCheck::new(
json_integer(required(fields, "variables")),
json_rows(required(fields, "rows")),
)
}
///|
fn config_from_json(value : Json) -> DecoderConfig raise CodeError {
let fields = object(value)
allowed(fields, [
"algorithm", "iterations", "normalization", "offset", "llr_limit",
])
let name = match fields.get("algorithm") {
Some(v) => json_string(v)
None => "sum-product"
}
let algorithm = match name {
"sum-product" => SumProduct
"min-sum" => MinSum
"layered-min-sum" => LayeredMinSum
_ => raise InvalidFormat(0, "unknown decoder algorithm")
}
let iterations = match fields.get("iterations") {
Some(v) => json_integer(v)
None => 50
}
let normalization = match fields.get("normalization") {
Some(v) => json_double(v)
None => 1.0
}
let offset = match fields.get("offset") {
Some(v) => json_double(v)
None => 0.0
}
let limit = match fields.get("llr_limit") {
Some(v) => json_double(v)
None => 50.0
}
DecoderConfig::new(
algorithm,
iterations~,
normalization~,
offset~,
llr_limit=limit,
)
}
///|
/// Host-neutral JSON protocol v1. Error output is structured; no files or network are accessed.
pub fn process_json(request : Json) -> Json raise CodeError {
let fields = object(request)
let action = json_string(required(fields, "action"))
let code = code_from_json(required(fields, "code"))
match action {
"analyze" => {
allowed(fields, ["action", "code"])
let encoder = Encoder::new(code)
let graph = code.analyze()
{
"ok": true,
"graph": graph.to_json(),
"rank": encoder.rank(),
"dimension": encoder.dimension(),
"girth": Json(code.girth()),
}
}
"encode" => {
allowed(fields, ["action", "code", "information"])
let encoder = Encoder::new(code)
let word = encoder.encode(json_ints(required(fields, "information")))
{
"ok": true,
"word": Json(word),
"information_positions": Json(encoder.information_positions()),
"syndrome": Json(code.syndrome(word)),
}
}
"decode" => {
allowed(fields, ["action", "code", "llr", "config"])
let config = match fields.get("config") {
Some(value) => config_from_json(value)
None => DecoderConfig::new(SumProduct)
}
let result = code.decode(json_doubles(required(fields, "llr")), config)
{ "ok": true, "result": result.to_json() }
}
"peel" | "recover" => {
allowed(fields, ["action", "code", "observations"])
let observations : Array[Int32?] = []
for value in json_array(required(fields, "observations")) {
if value is Null {
observations.push(None)
} else {
observations.push(Some(json_integer(value)))
}
}
let result = if action == "peel" {
code.peel(observations).to_json()
} else {
code.recover_erasures(observations).to_json()
}
{ "ok": true, "result": result }
}
"bit-flip" => {
allowed(fields, ["action", "code", "word", "iterations"])
let iterations = match fields.get("iterations") {
Some(v) => json_integer(v)
None => 50
}
let result = code.bit_flip(
json_ints(required(fields, "word")),
iterations~,
)
{ "ok": true, "result": result.to_json() }
}
"distance" => {
allowed(fields, ["action", "code"])
let result = Encoder::new(code).distance_report()
{ "ok": true, "result": result.to_json() }
}
"exact-decode" => {
allowed(fields, ["action", "code", "llr", "limit"])
let limit = match fields.get("limit") {
Some(v) => json_double(v)
None => 50.0
}
let result = Encoder::new(code).decode_exact(
json_doubles(required(fields, "llr")),
limit~,
)
{ "ok": true, "result": result.to_json() }
}
"nearest" => {
allowed(fields, ["action", "code", "word"])
let result = Encoder::new(code).nearest_word(
json_ints(required(fields, "word")),
)
{ "ok": true, "result": result.to_json() }
}
"correct-single" => {
allowed(fields, ["action", "code", "word"])
let result = code.correct_single(json_ints(required(fields, "word")))
{ "ok": true, "result": result.to_json() }
}
"stopping" => {
allowed(fields, ["action", "code", "variables"])
let result = code.stopping_set_report(
json_ints(required(fields, "variables")),
)
{ "ok": true, "result": result.to_json() }
}
"coset-encode" => {
allowed(fields, ["action", "code", "syndrome", "information"])
let encoder = CosetEncoder::new(
code,
json_ints(required(fields, "syndrome")),
)
let word = encoder.encode(json_ints(required(fields, "information")))
{ "ok": true, "word": Json(word), "syndrome": Json(code.syndrome(word)) }
}
"alist" => {
allowed(fields, ["action", "code"])
{ "ok": true, "alist": code.to_alist() }
}
_ => raise InvalidFormat(0, "unknown action")
}
}
///|
/// Errors are data, including JSON syntax errors. CLI exits zero on a protocol error;
/// automation must inspect the `ok` field, not treat process exit as decode success.
pub fn execute(request : String) -> String {
if request.length() > 16777216 {
return "{\"ok\":false,\"error\":\"request size limit\"}"
}
guard_json(request) catch {
_ =>
return "{\"ok\":false,\"error\":\"invalid JSON structure or duplicate key\"}"
}
let parsed = @json.parse(request) catch {
_ => return "{\"ok\":false,\"error\":\"invalid JSON\"}"
}
let response = process_json(parsed) catch {
error => {
let diagnostic = error.to_repr().to_string()
let result : Json = { "ok": false, "error": diagnostic }
return result.stringify()
}
}
response.stringify()
}