Skip to content

Commit 0a9c379

Browse files
authored
Merge pull request #147 from AdaWorldAPI/claude/distributed-db-xor-s3-Ep2Zi
feat: add XOR endpoint for RAID-1 mirror verification
2 parents df5c765 + 6f7d4e3 commit 0a9c379

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

src/bin/server.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,10 @@ fn route(
611611
("POST", "/api/v1/bind") => handle_bind(body, format),
612612
("POST", "/api/v1/bundle") => handle_bundle(body, format),
613613

614+
// XOR — nanosecond zero-copy parity
615+
("POST", "/api/v1/xor") => handle_xor(body, format),
616+
("POST", "/api/v1/xor/verify") => handle_xor_verify(body, format),
617+
614618
// Search
615619
("POST", "/api/v1/search/topk") => handle_topk(body, state, format),
616620
("POST", "/api/v1/search/threshold") => handle_threshold(body, state, format),
@@ -1048,6 +1052,90 @@ fn handle_bundle(body: &str, format: ResponseFormat) -> Vec<u8> {
10481052
}
10491053
}
10501054

1055+
/// POST /api/v1/xor — XOR two fingerprints. Single nanosecond operation.
1056+
fn handle_xor(body: &str, format: ResponseFormat) -> Vec<u8> {
1057+
let a_str = extract_json_str(body, "a").unwrap_or_default();
1058+
let b_str = extract_json_str(body, "b").unwrap_or_default();
1059+
let fp_a = resolve_fingerprint(&a_str);
1060+
let fp_b = resolve_fingerprint(&b_str);
1061+
1062+
// XOR: one pass over 256 u64 words — nanoseconds
1063+
let mut result = Fingerprint::zero();
1064+
for i in 0..FINGERPRINT_WORDS {
1065+
result.words_mut()[i] = fp_a.words()[i] ^ fp_b.words()[i];
1066+
}
1067+
1068+
match format {
1069+
ResponseFormat::Arrow => {
1070+
let schema = fingerprint_schema();
1071+
let batch = RecordBatch::try_new(
1072+
schema,
1073+
vec![
1074+
Arc::new(
1075+
FixedSizeBinaryArray::try_from_iter(std::iter::once(result.as_bytes()))
1076+
.unwrap(),
1077+
) as ArrayRef,
1078+
Arc::new(UInt32Array::from(vec![result.popcount()])) as ArrayRef,
1079+
Arc::new(Float32Array::from(vec![result.density()])) as ArrayRef,
1080+
Arc::new(UInt32Array::from(vec![FINGERPRINT_BITS as u32])) as ArrayRef,
1081+
],
1082+
)
1083+
.unwrap();
1084+
http_arrow(200, &batch)
1085+
}
1086+
ResponseFormat::Json => {
1087+
let json = format!(
1088+
r#"{{"result":"{}","popcount":{},"density":{:.4},"op":"xor"}}"#,
1089+
base64_encode(result.as_bytes()),
1090+
result.popcount(),
1091+
result.density()
1092+
);
1093+
http_json(200, &json)
1094+
}
1095+
}
1096+
}
1097+
1098+
/// POST /api/v1/xor/verify — verify RAID-1 parity: A ⊕ B == 0 means identical
1099+
fn handle_xor_verify(body: &str, format: ResponseFormat) -> Vec<u8> {
1100+
let a_str = extract_json_str(body, "a").unwrap_or_default();
1101+
let b_str = extract_json_str(body, "b").unwrap_or_default();
1102+
let fp_a = resolve_fingerprint(&a_str);
1103+
let fp_b = resolve_fingerprint(&b_str);
1104+
1105+
// XOR: identical fingerprints produce all zeros
1106+
let mut check = Fingerprint::zero();
1107+
for i in 0..FINGERPRINT_WORDS {
1108+
check.words_mut()[i] = fp_a.words()[i] ^ fp_b.words()[i];
1109+
}
1110+
let residual = check.popcount();
1111+
let valid = residual == 0;
1112+
1113+
match format {
1114+
ResponseFormat::Arrow => {
1115+
let schema = Arc::new(Schema::new(vec![
1116+
Field::new("valid", DataType::Boolean, false),
1117+
Field::new("residual_popcount", DataType::UInt32, false),
1118+
]));
1119+
let batch = RecordBatch::try_new(
1120+
schema,
1121+
vec![
1122+
Arc::new(BooleanArray::from(vec![valid])) as ArrayRef,
1123+
Arc::new(UInt32Array::from(vec![residual])) as ArrayRef,
1124+
],
1125+
)
1126+
.unwrap();
1127+
http_arrow(200, &batch)
1128+
}
1129+
ResponseFormat::Json => {
1130+
let json = format!(
1131+
r#"{{"valid":{},"residual_popcount":{},"op":"xor_verify"}}"#,
1132+
valid, residual
1133+
);
1134+
http_json(200, &json)
1135+
}
1136+
}
1137+
}
1138+
10511139
fn handle_topk(body: &str, state: &SharedState, format: ResponseFormat) -> Vec<u8> {
10521140
let query_str = extract_json_str(body, "query").unwrap_or_default();
10531141
let k = extract_json_usize(body, "k").unwrap_or(10);

0 commit comments

Comments
 (0)