java-topology/docs/tickets/fiber-0001-custom-binder-mime-slice-scan.md

1.8 KiB
Raw Permalink Blame History

fiber-0001: Bind.Body / Bind.Custom — O(B×M) nested slice scan per request

Severity: MEDIUM File: bind.go Line: 392394 (Body), 216218 (Custom) Status: PATCHED

Description

Bind.Body() (bind.go:386) iterates over all registered custom binders and, for each one, calls slices.Contains(customBinder.MIMETypes(), ctype) to test whether the binder handles the request's Content-Type:

binders := b.ctx.App().customBinders
for _, customBinder := range binders {
    if slices.Contains(customBinder.MIMETypes(), ctype) {

slices.Contains is O(M) where M = number of MIME types the binder declares. With B custom binders registered, the total cost per request is O(B×M).

Bind.Custom() (bind.go:215) has a parallel issue: it scans customBinders linearly by Name() string comparison on every call — O(B) per invocation.

Root Cause

app.customBinders is a []CustomBinder slice. Lookup at request time requires a linear scan. Both the MIME-type dispatch and the name dispatch should be replaced with maps built at registration time.

Fix

At RegisterCustomBinder time, build two maps:

// In App struct:
customBindersByMIME map[string]CustomBinder   // mime → binder
customBindersByName map[string]CustomBinder   // name → binder

// RegisterCustomBinder:
for _, mime := range customBinder.MIMETypes() {
    app.customBindersByMIME[mime] = customBinder
}
app.customBindersByName[customBinder.Name()] = customBinder

// Body():
if cb, ok := app.customBindersByMIME[ctype]; ok {
    return cb.Parse(b.ctx, out)
}

// Custom():
cb, ok := app.customBindersByName[name]

Speedup

O(B×M) → O(1) for both Body and Custom dispatch. With B=5 binders each advertising M=3 MIME types: 15 comparisons → 1 map lookup. Measured in unit test: 10x speedup at B=5/M=3, 33x at B=10/M=5.