[HN Gopher] Httpz - Zero-Allocation HTTP/1.1 Parser for OxCaml
___________________________________________________________________
Httpz - Zero-Allocation HTTP/1.1 Parser for OxCaml
Author : noelwelsh
Score : 71 points
Date : 2026-01-07 10:02 UTC (3 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| avsm wrote:
| (author here) I'm just adding data-race free parallelism support
| to this right now to switch my website over to using it! For
| those familiar with OCaml syntax, the OxCaml parse function is
| fun: val parse : buffer -> len:int -> #(status
| * request * header list) @ local
|
| This takes in a buffer and returns an unboxed tuple on the stack,
| so there's no GC activity involved beyond stack management for
| each HTTP request.
|
| https://github.com/avsm/httpz/blob/main/lib/httpz.mli#L154
| cdaringe wrote:
| Interesting parser, fun to read.
| henearkr wrote:
| Oh I got the joke! (I'm pretty sure it was intended)
|
| Yes a parser is a fun to read ;)
| ptrwis wrote:
| Doesn't (honest question) the operating system kernel prevent
| data races in memory accesses at the level of system calls like
| brk? I wonder at what level the operating system handles such
| things?
| ptrwis wrote:
| I mean, aren't system calls thread-safe?
| spooneybarger wrote:
| As a general rule, not all system calls are thread safe.
| gethly wrote:
| ocaml looks more like a spec than actual code.
| beckford wrote:
| If you were looking at the parse link in the author's comment,
| you were looking at a spec (called a module interface in
| OCaml/OxCaml, similar to an interface in Java). The parse
| implementation is at
| https://github.com/avsm/httpz/blob/240051dd5f00281b09984a14a...
|
| That said, I would be happy if all I needed to type in was a
| spec.
| nospice wrote:
| "Zero heap allocations: Parser results are stack-allocated using
| OxCaml unboxed records and local lists" - honest question, why?
|
| On almost any platform on which you want to run a HTTP server -
| including bare metal - it usually doesn't matter if you keep
| state near the stack pointer or not. What matters is that you use
| it well, making it play well with CPU caches, etc. Or is there
| something specifically horrible about OxCaml's heap allocator?
| avsm wrote:
| In a conventional GCed language, you need to minimise heap
| allocations to avoid putting too much pressure on the garbage
| collector. The OxCaml extensions allows values to be passed
| 'locally' (that is, on the callstack) as an alternative to heap
| allocation. When the function returns, the values are
| automatically deallocated (and the type system guarantees
| safety).
|
| This means that I can pass in a buffer, parse it, do my
| business logic, and then return, without ever allocating
| anything into the global heap. However, if I do need to
| allocate into it (for example, a complex structure), then it's
| still available.
|
| It's kind of Rust in reverse: OxCaml has a GC by default, but
| you can write very high-performance code that effectively never
| uses a GC. There's also emerging support for data-race-free
| parallelisation as well.
|
| The webserver I'm putting together also uses io_uring, which
| provides zero-copy buffers from kernel to userspace. The
| support for one-shot effect handlers in OCaml allows me to
| directly resume a blocked fiber straight from the io_uring
| loop, and then this httpz parser operates directly on that
| buffer. Shared memory all the way with almost no syscalls!
| ori_b wrote:
| Unboxed records are fine, but stack-allocated lists make me
| nervous. What happens when someone gives you 8 megs of headers,
| and you run out of stack?
|
| This code seems to put a 32k limit on it, but it's a manual
| check and error return. What about code that forgets to
| manually add that limit, or sets it too high? How do you decide
| when to bump that limit, since 32k is an artificial constraint?
| outpost_mystic2 wrote:
| By default in oxcaml, "stack" / local allocations happen in a
| separate stack on the heap (which the runtime allocates for
| you). If you allocate enough to exceed that capacity, it will
| resize it dynamically for you.
| naasking wrote:
| So stack-local arena. Neat.
| noelwelsh wrote:
| I think there are several advantages of stack allocation:
|
| * freeing stack allocated memory is O(1) with a small constant
| factor: simply set the stack pointer to a new location. In a
| generational garbage collector, like OCaml, minor garbage
| collection is O(amount of retained memory) with a larger
| constant factor.
|
| * judiciously stack allocating memory can improve data
| locality.
|
| * unboxed data takes up less space, again improving locality.
|
| Overall, I think this about improving constant factors---which
| makes a big difference in practice!
| infamouscow wrote:
| I'm excited to see what comes of OxCaml the next few years.
| messe wrote:
| > Local lists (@ local): Header list grows on the stack, not heap
|
| Does this mean unbounded stack growth? I'd much rather heap
| allocation if that's the case, as at least that can be recovered
| from in the case of allocation failure (assuming your OS,
| language, and stdlib allow for it).
| avsm wrote:
| This particular iteration is unbounded, but the next step is to
| pass in a GADT argument to specify which headers the
| application wants, so only those are parsed into a heterogenous
| tuple.
| messe wrote:
| That sounds like a rather elegant solution to it.
| spooneybarger wrote:
| The OxCaml work is great. I don't use OCaml much but I have been
| following along with OxCaml as they are doing fascinating work
| that leverages a lot of research that interests me.
___________________________________________________________________
(page generated 2026-01-10 23:00 UTC)