···5566open Result.Syntax
7788-(* {1 Internal Binary Utilities}
99-1010- Minimal reader/writer for in-memory byte operations.
1111- These are self-contained and don't depend on bytesrw. *)
1212-1313-type truncated = { need : int; have : int }
1414-1515-module Reader = struct
1616- type t = { buf : bytes; len : int; mutable pos : int }
1717-1818- let of_bytes buf = { buf; len = Bytes.length buf; pos = 0 }
1919- let remaining t = t.len - t.pos
2020-2121- let ensure t n =
2222- if remaining t >= n then Ok ()
2323- else Error (`Truncated { need = n; have = remaining t })
2424-2525- let uint8 t =
2626- if t.pos >= t.len then invalid_arg "Reader.uint8: need 1 byte, have 0";
2727- let b = Bytes.get_uint8 t.buf t.pos in
2828- t.pos <- t.pos + 1;
2929- b
3030-3131- let bytes t n =
3232- if t.pos + n > t.len then
3333- Fmt.invalid_arg "Reader.bytes: need %d bytes, have %d" n (remaining t);
3434- let result = Bytes.create n in
3535- Bytes.blit t.buf t.pos result 0 n;
3636- t.pos <- t.pos + n;
3737- result
3838-end
3939-4040-module Writer = struct
4141- type t = { buf : bytes; len : int; mutable pos : int; mutable max_pos : int }
4242-4343- let create n = { buf = Bytes.create n; len = n; pos = 0; max_pos = 0 }
4444- let update_max_pos t = if t.pos > t.max_pos then t.max_pos <- t.pos
4545-4646- let uint8 t v =
4747- if t.pos >= t.len then invalid_arg "Writer.uint8: buffer full";
4848- Bytes.set_uint8 t.buf t.pos (v land 0xFF);
4949- t.pos <- t.pos + 1;
5050- update_max_pos t
5151-5252- let bytes t data =
5353- let n = Bytes.length data in
5454- if t.pos + n > t.len then
5555- Fmt.invalid_arg "Writer.bytes: need %d bytes, have %d" n (t.len - t.pos);
5656- Bytes.blit data 0 t.buf t.pos n;
5757- t.pos <- t.pos + n;
5858- update_max_pos t
5959-6060- let contents t = Bytes.sub t.buf 0 t.max_pos
6161-end
6262-638(* {1 Callsigns} *)
6496510type ssid = int
···279224 done;
280225 !crc lxor 0xFFFF
281226227227+(* {1 Wire Codecs}
228228+229229+ Wire descriptions for the byte-level structures in AX.25 frames. *)
230230+231231+type ssid_byte = {
232232+ ch_flag : bool;
233233+ reserved : int;
234234+ ssid_val : int;
235235+ extension : bool;
236236+}
237237+(** SSID byte bitfield (1 byte): Bit 7: C/H flag (has_been_repeated) Bits 6-5:
238238+ reserved (always 0b11 = 0x60) Bits 4-1: SSID (4 bits) Bit 0: extension bit
239239+ (is_last) *)
240240+241241+let f_ch_flag = Wire.Field.v "ch_flag" (Wire.bit (Wire.bits ~width:1 Wire.U8))
242242+let f_reserved = Wire.Field.v "reserved" (Wire.bits ~width:2 Wire.U8)
243243+let f_ssid_val = Wire.Field.v "ssid_val" (Wire.bits ~width:4 Wire.U8)
244244+245245+let f_extension =
246246+ Wire.Field.v "extension" (Wire.bit (Wire.bits ~width:1 Wire.U8))
247247+248248+let ssid_codec =
249249+ Wire.Codec.v "SSID"
250250+ (fun ch_flag reserved ssid_val extension ->
251251+ { ch_flag; reserved; ssid_val; extension })
252252+ Wire.Codec.
253253+ [
254254+ (f_ch_flag $ fun s -> s.ch_flag);
255255+ (f_reserved $ fun s -> s.reserved);
256256+ (f_ssid_val $ fun s -> s.ssid_val);
257257+ (f_extension $ fun s -> s.extension);
258258+ ]
259259+260260+type callsign_wire = { call_bytes : string; ssid_byte : ssid_byte }
261261+(** Callsign wire record (7 bytes): 6 bytes shifted call + 1 byte SSID *)
262262+263263+let f_call_bytes =
264264+ Wire.Field.v "call_bytes" (Wire.byte_array ~size:(Wire.int 6))
265265+266266+let f_ssid_byte = Wire.Field.v "ssid_byte" (Wire.codec ssid_codec)
267267+268268+let callsign_codec =
269269+ Wire.Codec.v "Callsign"
270270+ (fun call_bytes ssid_byte -> { call_bytes; ssid_byte })
271271+ Wire.Codec.
272272+ [
273273+ (f_call_bytes $ fun c -> c.call_bytes);
274274+ (f_ssid_byte $ fun c -> c.ssid_byte);
275275+ ]
276276+277277+let callsign_wire_size = Wire.Codec.wire_size callsign_codec
278278+279279+(** Control byte: tagged union via map over uint8 *)
280280+let control_typ =
281281+ Wire.map ~decode:control_of_byte ~encode:byte_of_control Wire.uint8
282282+283283+(** PID byte: simple variant via map over uint8 *)
284284+let pid_typ = Wire.map ~decode:pid_of_byte ~encode:byte_of_pid Wire.uint8
285285+282286(* {1 Address Encoding}
283287284288 Each callsign is 7 bytes: 6 bytes for call (ASCII left-shifted by 1, space padded)
285289 plus 1 byte for SSID and flags. *)
286290287287-let encode_callsign w (cs : callsign) ~is_last ~has_been_repeated =
288288- (* Pad call to 6 chars with spaces, then left-shift each by 1 *)
289289- let padded = Fmt.str "%-6s" cs.call in
291291+let encode_call_string call =
292292+ (* Pad call to 6 chars with spaces, then left-shift each byte by 1 *)
293293+ let padded = Fmt.str "%-6s" call in
294294+ let buf = Bytes.create 6 in
290295 for i = 0 to 5 do
291291- Writer.uint8 w (Char.code padded.[i] lsl 1)
296296+ Bytes.set_uint8 buf i (Char.code padded.[i] lsl 1)
292297 done;
293293- (* SSID byte: bits 0 = extension, 1-4 = SSID, 5-6 = reserved (set to 1), 7 = C/H *)
294294- let ssid_byte =
295295- (if is_last then 0x01 else 0x00)
296296- lor ((cs.ssid land 0x0F) lsl 1)
297297- lor 0x60
298298- lor
299299- (* reserved bits = 11 *)
300300- if has_been_repeated then 0x80 else 0x00
301301- in
302302- Writer.uint8 w ssid_byte
298298+ Bytes.to_string buf
303299304304-let decode_callsign r ~is_digipeater:_ =
305305- let call_bytes = Reader.bytes r 6 in
306306- let call_buf = Buffer.create 6 in
300300+let decode_call_string raw =
301301+ (* Right-shift each byte by 1, strip trailing spaces *)
302302+ let buf = Buffer.create 6 in
307303 for i = 0 to 5 do
308308- let c = Char.chr (Char.code (Bytes.get call_bytes i) lsr 1) in
309309- if c <> ' ' then Buffer.add_char call_buf c
304304+ let c = Char.chr (Char.code raw.[i] lsr 1) in
305305+ if c <> ' ' then Buffer.add_char buf c
310306 done;
311311- let call = Buffer.contents call_buf in
312312- let ssid_byte = Reader.uint8 r in
313313- let ssid = (ssid_byte lsr 1) land 0x0F in
314314- let is_last = ssid_byte land 0x01 <> 0 in
315315- let _has_been_repeated = ssid_byte land 0x80 <> 0 in
316316- match callsign ~call ~ssid with
317317- | Some cs -> Ok (cs, is_last)
318318- | None -> Error (Invalid_callsign call)
307307+ Buffer.contents buf
308308+309309+let encode_callsign_to_bytes (cs : callsign) ~is_last ~has_been_repeated =
310310+ let cw =
311311+ {
312312+ call_bytes = encode_call_string cs.call;
313313+ ssid_byte =
314314+ {
315315+ ch_flag = has_been_repeated;
316316+ reserved = 0x03;
317317+ ssid_val = cs.ssid land 0x0F;
318318+ extension = is_last;
319319+ };
320320+ }
321321+ in
322322+ let buf = Bytes.create callsign_wire_size in
323323+ Wire.Codec.encode callsign_codec cw buf 0;
324324+ buf
325325+326326+let decode_callsign_from_bytes buf off =
327327+ match Wire.Codec.decode callsign_codec buf off with
328328+ | Error _ -> Error (Invalid_callsign "<decode error>")
329329+ | Ok cw -> (
330330+ let call = decode_call_string cw.call_bytes in
331331+ let ssid = cw.ssid_byte.ssid_val in
332332+ let is_last = cw.ssid_byte.extension in
333333+ match callsign ~call ~ssid with
334334+ | Some cs -> Ok (cs, is_last)
335335+ | None -> Error (Invalid_callsign call))
319336320337(* {1 Encoding/Decoding} *)
321338322322-let write w frame =
339339+let write_to_buf frame =
340340+ let buf = Buffer.create 512 in
323341 (* Destination *)
324324- encode_callsign w frame.address.destination
325325- ~is_last:(frame.address.source.ssid = 0 && frame.address.digipeaters = [])
326326- ~has_been_repeated:false;
342342+ let dst_bytes =
343343+ encode_callsign_to_bytes frame.address.destination
344344+ ~is_last:(frame.address.source.ssid = 0 && frame.address.digipeaters = [])
345345+ ~has_been_repeated:false
346346+ in
347347+ Buffer.add_bytes buf dst_bytes;
327348 (* Source *)
328328- let is_source_last = frame.address.digipeaters = [] in
329329- encode_callsign w frame.address.source ~is_last:is_source_last
330330- ~has_been_repeated:false;
349349+ let src_bytes =
350350+ encode_callsign_to_bytes frame.address.source
351351+ ~is_last:(frame.address.digipeaters = [])
352352+ ~has_been_repeated:false
353353+ in
354354+ Buffer.add_bytes buf src_bytes;
331355 (* Digipeaters *)
332356 let rec write_digis = function
333357 | [] -> ()
334334- | [ d ] -> encode_callsign w d ~is_last:true ~has_been_repeated:false
358358+ | [ d ] ->
359359+ Buffer.add_bytes buf
360360+ (encode_callsign_to_bytes d ~is_last:true ~has_been_repeated:false)
335361 | d :: rest ->
336336- encode_callsign w d ~is_last:false ~has_been_repeated:false;
362362+ Buffer.add_bytes buf
363363+ (encode_callsign_to_bytes d ~is_last:false ~has_been_repeated:false);
337364 write_digis rest
338365 in
339366 write_digis frame.address.digipeaters;
340367 (* Control *)
341341- Writer.uint8 w (byte_of_control frame.control);
368368+ let ctrl_buf = Wire.encode_to_bytes control_typ frame.control in
369369+ Buffer.add_bytes buf ctrl_buf;
342370 (* PID (for I and UI frames) *)
343371 (match frame.pid with
344344- | Some p -> Writer.uint8 w (byte_of_pid p)
372372+ | Some p ->
373373+ let pid_buf = Wire.encode_to_bytes pid_typ p in
374374+ Buffer.add_bytes buf pid_buf
345375 | None -> ());
346376 (* Info *)
347347- Writer.bytes w frame.info
377377+ Buffer.add_bytes buf frame.info;
378378+ Bytes.of_string (Buffer.contents buf)
348379349349-let read r =
350350- let ensure n =
351351- Reader.ensure r n
352352- |> Result.map_error (fun (`Truncated { have; need }) ->
353353- Truncated { have; need })
380380+let read_from_bytes data =
381381+ let len = Bytes.length data in
382382+ let ensure pos n =
383383+ if pos + n <= len then Ok ()
384384+ else Error (Truncated { need = n; have = len - pos })
354385 in
355386 (* Need at least 14 bytes for addresses (dest + src) + 1 for control *)
356356- let* () = ensure 15 in
387387+ let* () = ensure 0 15 in
388388+ let pos = ref 0 in
357389 (* Destination *)
358358- let* destination, _ = decode_callsign r ~is_digipeater:false in
390390+ let* destination, _ = decode_callsign_from_bytes data !pos in
391391+ pos := !pos + callsign_wire_size;
359392 (* Source *)
360360- let* source, is_last = decode_callsign r ~is_digipeater:false in
393393+ let* source, is_last = decode_callsign_from_bytes data !pos in
394394+ pos := !pos + callsign_wire_size;
361395 (* Digipeaters (if any) *)
362396 let rec read_digis acc =
363363- let* () = ensure 7 in
364364- let* digi, is_last_digi = decode_callsign r ~is_digipeater:true in
397397+ let* () = ensure !pos 7 in
398398+ let* digi, is_last_digi = decode_callsign_from_bytes data !pos in
399399+ pos := !pos + callsign_wire_size;
365400 if is_last_digi then Ok (List.rev (digi :: acc))
366401 else read_digis (digi :: acc)
367402 in
368403 let* digipeaters = if is_last then Ok [] else read_digis [] in
369404 let address = { destination; source; digipeaters } in
370405 (* Control *)
371371- let* () = ensure 1 in
372372- let control = control_of_byte (Reader.uint8 r) in
406406+ let* () = ensure !pos 1 in
407407+ let control =
408408+ match Wire.decode_bytes control_typ (Bytes.sub data !pos 1) with
409409+ | Ok c -> c
410410+ | Error _ -> control_of_byte (Bytes.get_uint8 data !pos)
411411+ in
412412+ pos := !pos + 1;
373413 (* PID (only for I and UI frames) *)
374414 let pid =
375415 match control with
376376- | UI | I _ -> (
377377- match Reader.ensure r 1 with
378378- | Error _ -> None
379379- | Ok () -> Some (pid_of_byte (Reader.uint8 r)))
416416+ | UI | I _ ->
417417+ if !pos < len then (
418418+ let p =
419419+ match Wire.decode_bytes pid_typ (Bytes.sub data !pos 1) with
420420+ | Ok p -> p
421421+ | Error _ -> pid_of_byte (Bytes.get_uint8 data !pos)
422422+ in
423423+ pos := !pos + 1;
424424+ Some p)
425425+ else None
380426 | _ -> None
381427 in
382428 (* Remaining is info field *)
383383- let info_len = Reader.remaining r in
384384- let info = if info_len > 0 then Reader.bytes r info_len else Bytes.empty in
429429+ let info_len = len - !pos in
430430+ let info =
431431+ if info_len > 0 then Bytes.sub data !pos info_len else Bytes.empty
432432+ in
385433 Ok { address; control; pid; info }
386434387435let encode frame =
388388- let w = Writer.create 512 in
389389- write w frame;
390390- let data = Writer.contents w in
436436+ let data = write_to_buf frame in
391437 (* Append FCS *)
392438 let fcs = crc_ccitt data in
393439 let result = Bytes.create (Bytes.length data + 2) in
···409455 let fcs_actual = fcs_lo lor (fcs_hi lsl 8) in
410456 if fcs_expected <> fcs_actual then
411457 Error (Invalid_fcs { expected = fcs_expected; actual = fcs_actual })
412412- else
413413- let r = Reader.of_bytes frame_data in
414414- read r
458458+ else read_from_bytes frame_data
415459416460(* {1 HDLC Framing}
417461···529573let kiss_tfesc = '\xDD'
530574531575let kiss_encode frame =
532532- let w = Writer.create 512 in
533533- write w frame;
534534- let data = Writer.contents w in
576576+ let data = write_to_buf frame in
535577 (* Escape special characters *)
536578 let buf = Buffer.create (Bytes.length data + 10) in
537579 Buffer.add_char buf kiss_fend;
···575617 incr i
576618 done;
577619 let frame_data = Bytes.of_string (Buffer.contents buf) in
578578- let r = Reader.of_bytes frame_data in
579579- read r
620620+ read_from_bytes frame_data
580621581622(* {1 Helpers} *)
582623