From 4681e861efcdf9b0b73e3803e70712bc55162863 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 10:38:18 +0900 Subject: feat(slides): improve slide controller --- .../index.html | 696 +++++++++++---------- 1 file changed, 353 insertions(+), 343 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2023-04-01') diff --git a/services/nuldoc/public/blog/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html b/services/nuldoc/public/blog/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html index a099d43c..30c08214 100644 --- a/services/nuldoc/public/blog/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html +++ b/services/nuldoc/public/blog/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html @@ -14,7 +14,7 @@ PNG 画像の最小構成エンコーダを実装する|REPL: Rest-Eat-Program Loop - +
@@ -135,44 +135,45 @@ 以下のソースコードをベースにする。今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ image/png を用いる。

-
package main -
-
import ( -
"image" -
_ "image/png" -
"io" -
"os" -
) -
-
func main() { -
inFile, err := os.Open("input.png") -
if err != nil { -
panic(err) -
} -
defer inFile.Close() -
-
img, _, err := image.Decode(inFile) -
if err != nil { -
panic(err) -
} -
-
outFile, err := os.Create("output.png") -
if err != nil { -
panic(err) -
} -
defer outFile.Close() -
-
writePng(outFile, img) -
} -
-
func writePng(w io.Writer, img image.Image) { -
width := uint32(img.Bounds().Dx()) -
height := uint32(img.Bounds().Dy()) -
writeSignature(w) -
writeChunkIhdr(w, width, height) -
writeChunkIdat(w, width, height, img) -
writeChunkIend(w) -
}
+
package main
+
+import (
+	"image"
+	_ "image/png"
+	"io"
+	"os"
+)
+
+func main() {
+	inFile, err := os.Open("input.png")
+	if err != nil {
+		panic(err)
+	}
+	defer inFile.Close()
+
+	img, _, err := image.Decode(inFile)
+	if err != nil {
+		panic(err)
+	}
+
+	outFile, err := os.Create("output.png")
+	if err != nil {
+		panic(err)
+	}
+	defer outFile.Close()
+
+	writePng(outFile, img)
+}
+
+func writePng(w io.Writer, img image.Image) {
+	width := uint32(img.Bounds().Dx())
+	height := uint32(img.Bounds().Dy())
+	writeSignature(w)
+	writeChunkIhdr(w, width, height)
+	writeChunkIdat(w, width, height, img)
+	writeChunkIend(w)
+}
+

以降は、writeSignaturewriteChunkIhdr などを実装していく。 @@ -215,21 +216,22 @@ writeSignature の実装はこちら:

-
import "encoding/binary" -
-
func writeSignature(w io.Writer) { -
sig := [8]uint8{ -
0x89, -
0x50, // P -
0x4E, // N -
0x47, // G -
0x0D, // CR -
0x0A, // LF -
0x1A, // EOF (^Z) -
0x0A, // LF -
} -
binary.Write(w, binary.BigEndian, sig) -
}
+
import "encoding/binary"
+
+func writeSignature(w io.Writer) {
+	sig := [8]uint8{
+		0x89,
+		0x50, // P
+		0x4E, // N
+		0x47, // G
+		0x0D, // CR
+		0x0A, // LF
+		0x1A, // EOF (^Z)
+		0x0A, // LF
+	}
+	binary.Write(w, binary.BigEndian, sig)
+}
+

encoding/binary パッケージの binary.Write を使い、固定の 8 バイトを書き込む。 @@ -258,55 +260,57 @@ CRC (Cyclic Redundancy Check) は誤り検出符号の一種。Go 言語では hash/crc32 パッケージにあるが、今回はこれも自前で実装する。PNG の仕様書に C 言語のサンプルコードが載っている ( D. Sample CRC implementation ) ので、これを Go に移植する。

-
var ( -
crcTable [256]uint32 -
crcTableComputed bool -
) -
-
func makeCrcTable() { -
for n := 0; n < 256; n++ { -
c := uint32(n) -
for k := 0; k < 8; k++ { -
if (c & 1) != 0 { -
c = 0xEDB88320 ^ (c >> 1) -
} else { -
c = c >> 1 -
} -
} -
crcTable[n] = c -
} -
crcTableComputed = true -
} -
-
func updateCrc(crc uint32, buf []byte) uint32 { -
if !crcTableComputed { -
makeCrcTable() -
} -
-
c := crc -
for n := 0; n < len(buf); n++ { -
c = crcTable[(c^uint32(buf[n]))&0xFF] ^ (c >> 8) -
} -
return c -
} -
-
func crc(buf []byte) uint32 { -
return updateCrc(0xFFFFFFFF, buf) ^ 0xFFFFFFFF -
}
+
var (
+	crcTable         [256]uint32
+	crcTableComputed bool
+)
+
+func makeCrcTable() {
+	for n := 0; n < 256; n++ {
+		c := uint32(n)
+		for k := 0; k < 8; k++ {
+			if (c & 1) != 0 {
+				c = 0xEDB88320 ^ (c >> 1)
+			} else {
+				c = c >> 1
+			}
+		}
+		crcTable[n] = c
+	}
+	crcTableComputed = true
+}
+
+func updateCrc(crc uint32, buf []byte) uint32 {
+	if !crcTableComputed {
+		makeCrcTable()
+	}
+
+	c := crc
+	for n := 0; n < len(buf); n++ {
+		c = crcTable[(c^uint32(buf[n]))&0xFF] ^ (c >> 8)
+	}
+	return c
+}
+
+func crc(buf []byte) uint32 {
+	return updateCrc(0xFFFFFFFF, buf) ^ 0xFFFFFFFF
+}
+

できた crc 関数を使って、chunk 一般を書き込む関数も用意しておこう。

-
func writeChunk(w io.Writer, chunkType string, data []byte) { -
typeAndData := make([]byte, 0, len(chunkType)+len(data)) -
typeAndData = append(typeAndData, []byte(chunkType)...) -
typeAndData = append(typeAndData, data...) -
-
binary.Write(w, binary.BigEndian, uint32(len(data))) -
binary.Write(w, binary.BigEndian, typeAndData) -
binary.Write(w, binary.BigEndian, crc(typeAndData)) -
}
+
func writeChunk(w io.Writer, chunkType string, data []byte) {
+	typeAndData := make([]byte, 0, len(chunkType)+len(data))
+	typeAndData = append(typeAndData, []byte(chunkType)...)
+	typeAndData = append(typeAndData, data...)
+
+	binary.Write(w, binary.BigEndian, uint32(len(data)))
+	binary.Write(w, binary.BigEndian, typeAndData)
+	binary.Write(w, binary.BigEndian, crc(typeAndData))
+}
+

仕様どおり、chunkTypedata から CRC を計算し、data の長さと合わせて書き込んでいる。PNG では基本的に big endian を使うことに注意する。 @@ -384,20 +388,21 @@ 今回ほとんどのデータは決め打ちするので、データに応じて変わるのは width と height だけになる。コードは次のようになる。

-
import "bytes" -
-
func writeChunkIhdr(w io.Writer, width, height uint32) { -
var buf bytes.Buffer -
binary.Write(&buf, binary.BigEndian, width) -
binary.Write(&buf, binary.BigEndian, height) -
binary.Write(&buf, binary.BigEndian, uint8(8)) -
binary.Write(&buf, binary.BigEndian, uint8(2)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
-
writeChunk(w, "IHDR", buf.Bytes()) -
}
+
import "bytes"
+
+func writeChunkIhdr(w io.Writer, width, height uint32) {
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, width)
+	binary.Write(&buf, binary.BigEndian, height)
+	binary.Write(&buf, binary.BigEndian, uint8(8))
+	binary.Write(&buf, binary.BigEndian, uint8(2))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+
+	writeChunk(w, "IHDR", buf.Bytes())
+}
+
@@ -431,22 +436,23 @@ Adler-32 も CRC と同じく誤り検出符号である。こちらも zlib の仕様書に C 言語でサンプルコードが記載されている ( 9. Appendix: Sample code ) ので、Go に移植する。

-
const adler32Base = 65521 -
-
func updateAdler32(adler uint32, buf []byte) uint32 { -
s1 := adler & 0xFFFF -
s2 := (adler >> 16) & 0xFFFF -
-
for n := 0; n < len(buf); n++ { -
s1 = (s1 + uint32(buf[n])) % adler32Base -
s2 = (s2 + s1) % adler32Base -
} -
return (s2 << 16) + s1 -
} -
-
func adler32(buf []byte) uint32 { -
return updateAdler32(1, buf) -
}
+
const adler32Base = 65521
+
+func updateAdler32(adler uint32, buf []byte) uint32 {
+	s1 := adler & 0xFFFF
+	s2 := (adler >> 16) & 0xFFFF
+
+	for n := 0; n < len(buf); n++ {
+		s1 = (s1 + uint32(buf[n])) % adler32Base
+		s2 = (s2 + s1) % adler32Base
+	}
+	return (s2 << 16) + s1
+}
+
+func adler32(buf []byte) uint32 {
+	return updateAdler32(1, buf)
+}
+

「データ」の部分には圧縮したデータが入るのだが、真面目に deflate アルゴリズムを実装する必要はない。Zlib には無圧縮のデータブロックを格納することができるので、これを使う。本来は、データの圧縮効率の悪いランダムなデータをそのまま格納するためのものだが、今回は deflate の実装をサボるために使う。 @@ -472,30 +478,31 @@ 実際にこの手抜き zlib を実装したものがこちら:

-
func encodeZlib(data []byte) []byte { -
var buf bytes.Buffer -
-
binary.Write(&buf, binary.BigEndian, uint8(0x78)) -
binary.Write(&buf, binary.BigEndian, uint8(0x01)) -
blockSize := 65535 -
isFinalBlock := false -
for i := 0; !isFinalBlock; i++ { -
var block []byte -
if len(data) <= (i+1)*blockSize { -
block = data[i*blockSize:] -
isFinalBlock = true -
} else { -
block = data[i*blockSize : (i+1)*blockSize] -
} -
binary.Write(&buf, binary.BigEndian, isFinalBlock) -
binary.Write(&buf, binary.LittleEndian, uint16(len(block))) -
binary.Write(&buf, binary.LittleEndian, uint16(^len(block))) -
binary.Write(&buf, binary.LittleEndian, block) -
} -
binary.Write(&buf, binary.BigEndian, adler32(data)) -
-
return buf.Bytes() -
}
+
func encodeZlib(data []byte) []byte {
+	var buf bytes.Buffer
+
+	binary.Write(&buf, binary.BigEndian, uint8(0x78))
+	binary.Write(&buf, binary.BigEndian, uint8(0x01))
+	blockSize := 65535
+	isFinalBlock := false
+	for i := 0; !isFinalBlock; i++ {
+		var block []byte
+		if len(data) <= (i+1)*blockSize {
+			block = data[i*blockSize:]
+			isFinalBlock = true
+		} else {
+			block = data[i*blockSize : (i+1)*blockSize]
+		}
+		binary.Write(&buf, binary.BigEndian, isFinalBlock)
+		binary.Write(&buf, binary.LittleEndian, uint16(len(block)))
+		binary.Write(&buf, binary.LittleEndian, uint16(^len(block)))
+		binary.Write(&buf, binary.LittleEndian, block)
+	}
+	binary.Write(&buf, binary.BigEndian, adler32(data))
+
+	return buf.Bytes()
+}
+
@@ -510,20 +517,21 @@ 先ほどの encodeZlib も使って実際に実装したものがこちら:

-
func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) { -
var pixels bytes.Buffer -
for y := uint32(0); y < height; y++ { -
binary.Write(&pixels, binary.BigEndian, uint8(0)) -
for x := uint32(0); x < width; x++ { -
r, g, b, _ := img.At(int(x), int(y)).RGBA() -
binary.Write(&pixels, binary.BigEndian, uint8(r)) -
binary.Write(&pixels, binary.BigEndian, uint8(g)) -
binary.Write(&pixels, binary.BigEndian, uint8(b)) -
} -
} -
-
writeChunk(w, "IDAT", encodeZlib(pixels.Bytes())) -
}
+
func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) {
+	var pixels bytes.Buffer
+	for y := uint32(0); y < height; y++ {
+		binary.Write(&pixels, binary.BigEndian, uint8(0))
+		for x := uint32(0); x < width; x++ {
+			r, g, b, _ := img.At(int(x), int(y)).RGBA()
+			binary.Write(&pixels, binary.BigEndian, uint8(r))
+			binary.Write(&pixels, binary.BigEndian, uint8(g))
+			binary.Write(&pixels, binary.BigEndian, uint8(b))
+		}
+	}
+
+	writeChunk(w, "IDAT", encodeZlib(pixels.Bytes()))
+}
+
@@ -536,9 +544,10 @@ 特に追加のデータはなく、必要なのは chunk type の IEND くらいなので実装は簡単:

-
func writeChunkIend(w io.Writer) { -
writeChunk(w, "IEND", nil) -
}
+
func writeChunkIend(w io.Writer) {
+	writeChunk(w, "IEND", nil)
+}
+
@@ -548,180 +557,181 @@ 最後に全ソースコードを再掲しておく。

-
package main -
-
import ( -
"bytes" -
"encoding/binary" -
"image" -
_ "image/png" -
"io" -
"os" -
) -
-
func main() { -
inFile, err := os.Open("input.png") -
if err != nil { -
panic(err) -
} -
defer inFile.Close() -
-
img, _, err := image.Decode(inFile) -
if err != nil { -
panic(err) -
} -
-
outFile, err := os.Create("output.png") -
if err != nil { -
panic(err) -
} -
defer outFile.Close() -
-
writePng(outFile, img) -
} -
-
func writePng(w io.Writer, img image.Image) { -
width := uint32(img.Bounds().Dx()) -
height := uint32(img.Bounds().Dy()) -
writeSignature(w) -
writeChunkIhdr(w, width, height) -
writeChunkIdat(w, width, height, img) -
writeChunkIend(w) -
} -
-
func writeSignature(w io.Writer) { -
sig := [8]uint8{ -
0x89, -
0x50, // P -
0x4E, // N -
0x47, // G -
0x0D, // CR -
0x0A, // LF -
0x1A, // EOF (^Z) -
0x0A, // LF -
} -
binary.Write(w, binary.BigEndian, sig) -
} -
-
func writeChunkIhdr(w io.Writer, width, height uint32) { -
var buf bytes.Buffer -
binary.Write(&buf, binary.BigEndian, width) -
binary.Write(&buf, binary.BigEndian, height) -
binary.Write(&buf, binary.BigEndian, uint8(8)) -
binary.Write(&buf, binary.BigEndian, uint8(2)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
binary.Write(&buf, binary.BigEndian, uint8(0)) -
-
writeChunk(w, "IHDR", buf.Bytes()) -
} -
-
func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) { -
var pixels bytes.Buffer -
for y := uint32(0); y < height; y++ { -
binary.Write(&pixels, binary.BigEndian, uint8(0)) -
for x := uint32(0); x < width; x++ { -
r, g, b, _ := img.At(int(x), int(y)).RGBA() -
binary.Write(&pixels, binary.BigEndian, uint8(r)) -
binary.Write(&pixels, binary.BigEndian, uint8(g)) -
binary.Write(&pixels, binary.BigEndian, uint8(b)) -
} -
} -
-
writeChunk(w, "IDAT", encodeZlib(pixels.Bytes())) -
} -
-
func encodeZlib(data []byte) []byte { -
var buf bytes.Buffer -
-
binary.Write(&buf, binary.BigEndian, uint8(0x78)) -
binary.Write(&buf, binary.BigEndian, uint8(0x01)) -
blockSize := 65535 -
isFinalBlock := false -
for i := 0; !isFinalBlock; i++ { -
var block []byte -
if len(data) <= (i+1)*blockSize { -
block = data[i*blockSize:] -
isFinalBlock = true -
} else { -
block = data[i*blockSize : (i+1)*blockSize] -
} -
binary.Write(&buf, binary.BigEndian, isFinalBlock) -
binary.Write(&buf, binary.LittleEndian, uint16(len(block))) -
binary.Write(&buf, binary.LittleEndian, uint16(^len(block))) -
binary.Write(&buf, binary.LittleEndian, block) -
} -
binary.Write(&buf, binary.BigEndian, adler32(data)) -
-
return buf.Bytes() -
} -
-
func writeChunkIend(w io.Writer) { -
writeChunk(w, "IEND", nil) -
} -
-
func writeChunk(w io.Writer, chunkType string, data []byte) { -
typeAndData := make([]byte, 0, len(chunkType)+len(data)) -
typeAndData = append(typeAndData, []byte(chunkType)...) -
typeAndData = append(typeAndData, data...) -
-
binary.Write(w, binary.BigEndian, uint32(len(data))) -
binary.Write(w, binary.BigEndian, typeAndData) -
binary.Write(w, binary.BigEndian, crc(typeAndData)) -
} -
-
var ( -
crcTable [256]uint32 -
crcTableComputed bool -
) -
-
func makeCrcTable() { -
for n := 0; n < 256; n++ { -
c := uint32(n) -
for k := 0; k < 8; k++ { -
if (c & 1) != 0 { -
c = 0xEDB88320 ^ (c >> 1) -
} else { -
c = c >> 1 -
} -
} -
crcTable[n] = c -
} -
crcTableComputed = true -
} -
-
func updateCrc(crc uint32, buf []byte) uint32 { -
if !crcTableComputed { -
makeCrcTable() -
} -
-
c := crc -
for n := 0; n < len(buf); n++ { -
c = crcTable[(c^uint32(buf[n]))&0xFF] ^ (c >> 8) -
} -
return c -
} -
-
func crc(buf []byte) uint32 { -
return updateCrc(0xFFFFFFFF, buf) ^ 0xFFFFFFFF -
} -
-
const adler32Base = 65521 -
-
func updateAdler32(adler uint32, buf []byte) uint32 { -
s1 := adler & 0xFFFF -
s2 := (adler >> 16) & 0xFFFF -
-
for n := 0; n < len(buf); n++ { -
s1 = (s1 + uint32(buf[n])) % adler32Base -
s2 = (s2 + s1) % adler32Base -
} -
return (s2 << 16) + s1 -
} -
-
func adler32(buf []byte) uint32 { -
return updateAdler32(1, buf) -
}
+
package main
+
+import (
+	"bytes"
+	"encoding/binary"
+	"image"
+	_ "image/png"
+	"io"
+	"os"
+)
+
+func main() {
+	inFile, err := os.Open("input.png")
+	if err != nil {
+		panic(err)
+	}
+	defer inFile.Close()
+
+	img, _, err := image.Decode(inFile)
+	if err != nil {
+		panic(err)
+	}
+
+	outFile, err := os.Create("output.png")
+	if err != nil {
+		panic(err)
+	}
+	defer outFile.Close()
+
+	writePng(outFile, img)
+}
+
+func writePng(w io.Writer, img image.Image) {
+	width := uint32(img.Bounds().Dx())
+	height := uint32(img.Bounds().Dy())
+	writeSignature(w)
+	writeChunkIhdr(w, width, height)
+	writeChunkIdat(w, width, height, img)
+	writeChunkIend(w)
+}
+
+func writeSignature(w io.Writer) {
+	sig := [8]uint8{
+		0x89,
+		0x50, // P
+		0x4E, // N
+		0x47, // G
+		0x0D, // CR
+		0x0A, // LF
+		0x1A, // EOF (^Z)
+		0x0A, // LF
+	}
+	binary.Write(w, binary.BigEndian, sig)
+}
+
+func writeChunkIhdr(w io.Writer, width, height uint32) {
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, width)
+	binary.Write(&buf, binary.BigEndian, height)
+	binary.Write(&buf, binary.BigEndian, uint8(8))
+	binary.Write(&buf, binary.BigEndian, uint8(2))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+	binary.Write(&buf, binary.BigEndian, uint8(0))
+
+	writeChunk(w, "IHDR", buf.Bytes())
+}
+
+func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) {
+	var pixels bytes.Buffer
+	for y := uint32(0); y < height; y++ {
+		binary.Write(&pixels, binary.BigEndian, uint8(0))
+		for x := uint32(0); x < width; x++ {
+			r, g, b, _ := img.At(int(x), int(y)).RGBA()
+			binary.Write(&pixels, binary.BigEndian, uint8(r))
+			binary.Write(&pixels, binary.BigEndian, uint8(g))
+			binary.Write(&pixels, binary.BigEndian, uint8(b))
+		}
+	}
+
+	writeChunk(w, "IDAT", encodeZlib(pixels.Bytes()))
+}
+
+func encodeZlib(data []byte) []byte {
+	var buf bytes.Buffer
+
+	binary.Write(&buf, binary.BigEndian, uint8(0x78))
+	binary.Write(&buf, binary.BigEndian, uint8(0x01))
+	blockSize := 65535
+	isFinalBlock := false
+	for i := 0; !isFinalBlock; i++ {
+		var block []byte
+		if len(data) <= (i+1)*blockSize {
+			block = data[i*blockSize:]
+			isFinalBlock = true
+		} else {
+			block = data[i*blockSize : (i+1)*blockSize]
+		}
+		binary.Write(&buf, binary.BigEndian, isFinalBlock)
+		binary.Write(&buf, binary.LittleEndian, uint16(len(block)))
+		binary.Write(&buf, binary.LittleEndian, uint16(^len(block)))
+		binary.Write(&buf, binary.LittleEndian, block)
+	}
+	binary.Write(&buf, binary.BigEndian, adler32(data))
+
+	return buf.Bytes()
+}
+
+func writeChunkIend(w io.Writer) {
+	writeChunk(w, "IEND", nil)
+}
+
+func writeChunk(w io.Writer, chunkType string, data []byte) {
+	typeAndData := make([]byte, 0, len(chunkType)+len(data))
+	typeAndData = append(typeAndData, []byte(chunkType)...)
+	typeAndData = append(typeAndData, data...)
+
+	binary.Write(w, binary.BigEndian, uint32(len(data)))
+	binary.Write(w, binary.BigEndian, typeAndData)
+	binary.Write(w, binary.BigEndian, crc(typeAndData))
+}
+
+var (
+	crcTable         [256]uint32
+	crcTableComputed bool
+)
+
+func makeCrcTable() {
+	for n := 0; n < 256; n++ {
+		c := uint32(n)
+		for k := 0; k < 8; k++ {
+			if (c & 1) != 0 {
+				c = 0xEDB88320 ^ (c >> 1)
+			} else {
+				c = c >> 1
+			}
+		}
+		crcTable[n] = c
+	}
+	crcTableComputed = true
+}
+
+func updateCrc(crc uint32, buf []byte) uint32 {
+	if !crcTableComputed {
+		makeCrcTable()
+	}
+
+	c := crc
+	for n := 0; n < len(buf); n++ {
+		c = crcTable[(c^uint32(buf[n]))&0xFF] ^ (c >> 8)
+	}
+	return c
+}
+
+func crc(buf []byte) uint32 {
+	return updateCrc(0xFFFFFFFF, buf) ^ 0xFFFFFFFF
+}
+
+const adler32Base = 65521
+
+func updateAdler32(adler uint32, buf []byte) uint32 {
+	s1 := adler & 0xFFFF
+	s2 := (adler >> 16) & 0xFFFF
+
+	for n := 0; n < len(buf); n++ {
+		s1 = (s1 + uint32(buf[n])) % adler32Base
+		s2 = (s2 + s1) % adler32Base
+	}
+	return (s2 << 16) + s1
+}
+
+func adler32(buf []byte) uint32 {
+	return updateAdler32(1, buf)
+}
+
-- cgit v1.3.1