Pipeline, measured per-stage sizes (113,015 -> 101,498 -> 34,784 bytes) and how to verify a build against the embedded image.
3.4 KiB
Compressing the Web UI Assets
The Web UI (all files under html/) is embedded into the flash image and
served over HTTP. Two build-time steps keep it small: a safe minifier and
gzip compression of the embedded files.
How it works
html/ --(minify.py)--> output/html_min/ --(fileadder -z)--> flash image
tools/minify.pyremoves comments (<!-- -->,//,/* */) and line-leading whitespace without touching string literals, so the output is functionally identical to the input. Binary files (.ico) are copied through unchanged. The raw sources inhtml/stay untouched for development; the minified copy inoutput/is a pure build artifact. Comments and indentation are pure overhead for the served bytes, so minification alone already reduces the transfer size from 113,015 to 101,498 bytes, independent of gzip.fileadder -zgzip-compresses every file (zlib, gzip format,Z_BEST_COMPRESSION) when it generates the file table (html_data.c/html_data.h) and when it embeds the files into the final image. Both invocations run onoutput/html_min/, so the table and the image always agree.- The httpd serves each file with
Content-Encoding: gzipaccording to thegzipflag in itsf_dataentry. The 8051 only copies compressed bytes from flash to the socket; decompression happens in the browser. - Only the static files are compressed. The JSON API responses
(
/information.json,/vlanlist, ...) are generated on the fly and stay uncompressed.
Measured effect
Build on any machine (the Web UI is machine-independent):
| bytes | |
|---|---|
| raw sources | 113,015 |
| minified (comments and indentation removed) | 101,498 |
| gzip (flash + wire) | 34,784 (30.8 %) |
The minified assets are also what the browser receives when gzip support is missing or disabled, so the transfer size drops in both stages: 113,015 → 101,498 bytes from minification, then → 34,784 bytes from gzip.
The embedded data block ends at 0x5b977 without compression and at
0x487e0 with it, freeing ~78 KB of the 512 KB image.
Notes
- The files stay well below the
uint16_tsize limit of the file table (largest gzip output: 5.4 KB fori18n.js). fileadderterminates the embedded files with a NUL directly after the content (previously atdata_read + 1), so thestrlen()-based size computation no longer depends on uninitialised buffer content.version.his no longer.PHONY; every build previously rewrote it while a parallel build (make -j) could read a half-written copy.
Verification
Decompress what the firmware embeds and compare it with the minified sources:
import gzip, re
h = open('html_data.h').read(); c = open('html_data.c').read()
starts = dict((k, int(v, 16)) for k, v in
re.findall(r'#define FDATA_START_(\w+) 0x([0-9a-f]+)', h))
sizes = dict((k, int(v)) for k, v in
re.findall(r'#define FDATA_SIZE_(\w+) (\d+)', h))
entries = [(p, sizes[z], starts[s]) for p, s, z in
re.findall(r'\{"/([^"]+)", FDATA_START_(\w+), '
r'FDATA_SIZE_(\w+), \w+, 1\}', c)]
d = open('output/rtlplayground.bin', 'rb').read()
for name, size, start in entries:
assert gzip.decompress(d[start:start+size]) == \
open('output/html_min/' + name, 'rb').read()
tools/httpd_sim serves the raw files from html/ and does not exercise
the gzip path; a browser session against a flashed switch (or curl --compressed) is the final check.