summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-12-28 16:22:29 +0700
committerShulhan <ms@kilabit.info>2024-12-28 16:46:59 +0700
commitc1aab5c376dfa1845b839f1c4b9876a1412a8d24 (patch)
treea4d191420f52a3f1aad25c4609ced5e03036e5ab
parent4003b6359747f6e43357e4bf190d4e71a66ec796 (diff)
downloadpakakeh.go-c1aab5c376dfa1845b839f1c4b9876a1412a8d24.tar.xz
lib/bytes: split the hexdump related functions to separate package
Package hexdump implements reading and writing bytes from and into hexadecimal number. It support parsing output from hexdump(1) tool.
-rw-r--r--README.md5
-rw-r--r--lib/binary/apo_file_test.go7
-rw-r--r--lib/bytes/bytes.go205
-rw-r--r--lib/bytes/bytes_example_test.go47
-rw-r--r--lib/bytes/bytes_test.go70
-rw-r--r--lib/dns/message_test.go16
-rw-r--r--lib/dns/zone_test.go12
-rw-r--r--lib/hexdump/hexdump.go217
-rw-r--r--lib/hexdump/hexdump_example_test.go61
-rw-r--r--lib/hexdump/hexdump_test.go76
-rw-r--r--lib/hexdump/testdata/Parse_exp_dir_tar_test.txt (renamed from lib/bytes/testdata/ParseHexDump_exp_dir_tar_test.txt)4
-rw-r--r--lib/hexdump/testdata/Parse_test.txt (renamed from lib/bytes/testdata/ParseHexDump_test.txt)4
-rw-r--r--lib/hexdump/testdata/exp_dir.tar (renamed from lib/bytes/testdata/exp_dir.tar)bin10240 -> 10240 bytes
-rw-r--r--lib/hexdump/testdata/exp_dir.tar.license3
-rw-r--r--lib/memfs/internal/test/embed/embed_test.go38
15 files changed, 410 insertions, 355 deletions
diff --git a/README.md b/README.md
index 1e94ca10..3a3d440b 100644
--- a/README.md
+++ b/README.md
@@ -102,6 +102,11 @@ A library for working with slice of float64.
[**git**](https://pkg.go.dev/git.sr.ht/~shulhan/pakakeh.go/lib/git)::
A wrapper for git command line interface.
+[**hexdump**](https://pkg.go.dev/git.sr.ht/~shulhan/pakakeh.go/lib/hexdump)::
+Package hexdump implements reading and writing bytes from and into
+hexadecimal number.
+It support parsing output from hexdump(1) tool.
+
[**http**](https://pkg.go.dev/git.sr.ht/~shulhan/pakakeh.go/lib/http)::
Package http extends the standard http package with simplified routing handler
and builtin memory file system.
diff --git a/lib/binary/apo_file_test.go b/lib/binary/apo_file_test.go
index 52d54500..ac5912dc 100644
--- a/lib/binary/apo_file_test.go
+++ b/lib/binary/apo_file_test.go
@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
// SPDX-License-Identifier: BSD-3-Clause
package binary
@@ -9,7 +10,7 @@ import (
"path/filepath"
"testing"
- libbytes "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/hexdump"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
@@ -47,7 +48,7 @@ func TestOpenApo(t *testing.T) {
}
var gotDump bytes.Buffer
- libbytes.DumpPrettyTable(&gotDump, `empty`, gotb)
+ hexdump.PrettyPrint(&gotDump, `empty`, gotb)
var exp = string(tdata.Output[`empty`])
test.Assert(t, `empty`, exp, gotDump.String())
@@ -141,7 +142,7 @@ func testWrite(t *testing.T, tcase testCaseWrite, apow *ApoFile) {
}
var gotDump bytes.Buffer
- libbytes.DumpPrettyTable(&gotDump, tcase.tag, gotb)
+ hexdump.PrettyPrint(&gotDump, tcase.tag, gotb)
test.Assert(t, tcase.tag, tcase.expHexdump, gotDump.String())
}
diff --git a/lib/bytes/bytes.go b/lib/bytes/bytes.go
index b47b0a85..3bbf529b 100644
--- a/lib/bytes/bytes.go
+++ b/lib/bytes/bytes.go
@@ -7,9 +7,6 @@ package bytes
import (
"bytes"
- "fmt"
- "io"
- "strconv"
"unicode"
inbytes "git.sr.ht/~shulhan/pakakeh.go/internal/bytes"
@@ -323,156 +320,6 @@ func MergeSpaces(in []byte) (out []byte) {
return out
}
-// ParseHexDump parse the default output of [hexdump](1) utility from
-// parameter in back into stream of byte.
-//
-// An example of default output of hexdump is
-//
-// 0000000 7865 5f70 6964 2f72 0000 0000 0000 0000
-// 0000010 0000 0000 0000 0000 0000 0000 0000 0000
-// *
-// 0000060 0000 0000 3030 3030 3537 0035 3030 3130
-//
-// The first column is the address and the rest of the column is the data.
-//
-// Each data column is 16-bit words in big-endian order, so in the above
-// example, the first byte would be 65, second byte is 78 and so on.
-// If parameter networkByteOrder is true, the first byte would be 78, second
-// by is 65, and so on.
-//
-// The asterisk "*" means that the address from 0000020 to 0000050 is equal
-// to the previous line, 0000010.
-//
-// [hexdump]: https://man.archlinux.org/man/hexdump.1
-func ParseHexDump(in []byte, networkByteOrder bool) (out []byte, err error) {
- var (
- logp = `ParseHexDump`
- parser = NewParser(in, []byte(" \n"))
- d byte = 255 // Just to make the first for-loop pass.
-
- token []byte
- vint64 int64
- x int
- isAsterisk bool
- )
- for d != 0 {
- // Read the address.
- token, d = parser.Read()
- if len(token) == 0 {
- break
- }
- if len(token) == 1 {
- if token[0] != '*' {
- break
- }
- isAsterisk = true
- continue
- }
-
- vint64, err = strconv.ParseInt(string(token), 16, 64)
- if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
- }
-
- if isAsterisk {
- if len(out) > 0 {
- var start = len(out)
- if start < 16 {
- start = 0
- } else {
- start -= 16
- }
- var (
- prevRow = out[start:]
- identicalRow = int((vint64 - int64(len(out))) / 16)
- )
- for x = 0; x < identicalRow; x++ {
- out = append(out, prevRow...)
- }
- }
- }
-
- // Read the two-hex, 16-bit words.
- for x = 0; x < 8; x++ {
- token, d = parser.Read()
- if len(token) == 0 {
- break
- }
-
- vint64, err = strconv.ParseInt(string(token), 16, 64)
- if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
- }
-
- switch len(token) {
- case 2:
- out = append(out, byte(vint64))
- case 4:
- if networkByteOrder {
- out = append(out, byte(vint64>>8))
- out = append(out, byte(vint64))
- } else {
- out = append(out, byte(vint64))
- out = append(out, byte(vint64>>8))
- }
- }
-
- if d == '\n' {
- break
- }
- }
- // Ignore trailing characters.
- if d != '\n' {
- parser.SkipLine()
- }
- }
- return out, nil
-}
-
-// PrintHex will print each byte in slice as hexadecimal value into N column
-// length.
-func PrintHex(title string, data []byte, col int) {
- var (
- start, x int
- c byte
- )
- fmt.Print(title)
- for x, c = range data {
- if x%col == 0 {
- if x > 0 {
- fmt.Print(" ||")
- }
- for y := start; y < x; y++ {
- if data[y] >= 33 && data[y] <= 126 {
- fmt.Printf(" %c", data[y])
- } else {
- fmt.Print(" .")
- }
- }
- fmt.Printf("\n%4d -", x)
- start = x
- }
-
- fmt.Printf(" %02X", c)
- }
- rest := col - (x % col)
- if rest > 0 {
- for y := 1; y < rest; y++ {
- fmt.Print(" ")
- }
- fmt.Print(" ||")
- }
- for y := start; y <= x; y++ {
- if data[y] >= 33 && data[y] <= 126 {
- fmt.Printf(" %c", data[y])
- } else {
- fmt.Print(" .")
- }
- }
-
- fmt.Println()
-}
-
// ReadHexByte read two hexadecimal characters from "data" start from index
// "x" and convert them to byte.
// It will return the byte and true if its read exactly two hexadecimal
@@ -722,58 +569,6 @@ func WordIndexes(s []byte, word []byte) (idxs []int) {
return idxs
}
-// DumpPrettyTable write each byte in slice data as hexadecimal, ASCII
-// character, and integer with 8 columns width.
-func DumpPrettyTable(w io.Writer, title string, data []byte) {
- const ncol = 8
-
- fmt.Fprintf(w, "%s\n", title)
- fmt.Fprint(w, " | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |\n")
- fmt.Fprint(w, " | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |\n")
-
- var (
- chunks = SplitEach(data, ncol)
- chunk []byte
- x int
- y int
- c byte
- )
- for x, chunk = range chunks {
- fmt.Fprintf(w, `%#08x|`, x*ncol)
-
- // Print as hex.
- for y, c = range chunk {
- fmt.Fprintf(w, ` %02x`, c)
- }
- for y++; y < ncol; y++ {
- fmt.Fprint(w, ` `)
- }
-
- // Print as char.
- fmt.Fprint(w, ` | `)
- for y, c = range chunk {
- if c >= 33 && c <= 126 {
- fmt.Fprintf(w, `%c`, c)
- } else {
- fmt.Fprint(w, `.`)
- }
- }
- for y++; y < ncol; y++ {
- fmt.Fprint(w, ` `)
- }
-
- // Print as integer.
- fmt.Fprint(w, ` |`)
- for y, c = range chunk {
- fmt.Fprintf(w, ` %3d`, c)
- }
- for y++; y < ncol; y++ {
- fmt.Fprint(w, ` `)
- }
- fmt.Fprintf(w, " |%d\n", x*ncol)
- }
-}
-
// WriteUint16 write uint16 value "v" into "data" start at position "x".
// If x is out range, the data will not change.
func WriteUint16(data []byte, x uint, v uint16) {
diff --git a/lib/bytes/bytes_example_test.go b/lib/bytes/bytes_example_test.go
index 0c2fbf49..6615f36d 100644
--- a/lib/bytes/bytes_example_test.go
+++ b/lib/bytes/bytes_example_test.go
@@ -5,9 +5,7 @@
package bytes_test
import (
- "bytes"
"fmt"
- "log"
"math"
"git.sr.ht/~shulhan/pakakeh.go/lib/ascii"
@@ -305,35 +303,6 @@ func ExampleMergeSpaces() {
// a
}
-func ExampleParseHexDump() {
- var (
- in = []byte("0000000 6548 6c6c 2c6f 7720 726f 646c 0021")
-
- out []byte
- err error
- )
-
- out, err = libbytes.ParseHexDump(in, false)
- if err != nil {
- log.Fatal(err)
- }
-
- fmt.Printf(`%s`, libbytes.TrimNull(out))
- // Output:
- // Hello, world!
-}
-
-func ExamplePrintHex() {
- title := "PrintHex"
- data := []byte("Hello, world !")
- libbytes.PrintHex(title, data, 5)
- // Output:
- // PrintHex
- // 0 - 48 65 6C 6C 6F || H e l l o
- // 5 - 2C 20 77 6F 72 || , . w o r
- // 10 - 6C 64 20 21 || l d . !
-}
-
func ExampleReadHexByte() {
fmt.Println(libbytes.ReadHexByte([]byte{}, 0))
fmt.Println(libbytes.ReadHexByte([]byte("x0"), 0))
@@ -492,22 +461,6 @@ func ExampleWordIndexes() {
// []
}
-func ExampleDumpPrettyTable() {
- var (
- data = []byte{1, 2, 3, 'H', 'e', 'l', 'l', 'o', 254, 255}
- bb bytes.Buffer
- )
-
- libbytes.DumpPrettyTable(&bb, `DumpPrettyTable`, data)
- fmt.Println(bb.String())
- // Output:
- // DumpPrettyTable
- // | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |
- // | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |
- // 0x00000000| 01 02 03 48 65 6c 6c 6f | ...Hello | 1 2 3 72 101 108 108 111 |0
- // 0x00000008| fe ff | .. | 254 255 |8
-}
-
func ExampleWriteUint16() {
data := []byte("Hello, world!")
diff --git a/lib/bytes/bytes_test.go b/lib/bytes/bytes_test.go
index 94d474f0..d41e399d 100644
--- a/lib/bytes/bytes_test.go
+++ b/lib/bytes/bytes_test.go
@@ -1,80 +1,16 @@
-// Copyright 2023, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
package bytes
import (
"fmt"
- "os"
- "path"
"testing"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
-func TestParseHexDump(t *testing.T) {
- var (
- tdata *test.Data
- err error
- )
-
- tdata, err = test.LoadData(`testdata/ParseHexDump_test.txt`)
- if err != nil {
- t.Fatal(err)
- }
-
- var (
- tag string
- in []byte
- exp []byte
- got []byte
- )
- for tag, in = range tdata.Input {
- exp = tdata.Output[tag]
-
- got, err = ParseHexDump(in, false)
- if err != nil {
- test.Assert(t, tag, string(exp), err.Error())
- continue
- }
-
- test.Assert(t, tag, string(exp), string(got))
- }
-}
-
-func TestParseHexDumpExpDirTar(t *testing.T) {
- var (
- tdata *test.Data
- err error
- )
-
- tdata, err = test.LoadData(`testdata/ParseHexDump_exp_dir_tar_test.txt`)
- if err != nil {
- t.Fatal(err)
- }
-
- var (
- tag = `exp_dir.tar`
- expFile = path.Join(`testdata`, tag)
-
- exp []byte
- got []byte
- )
-
- got, err = ParseHexDump(tdata.Input[tag], false)
- if err != nil {
- t.Fatal(err)
- }
-
- exp, err = os.ReadFile(expFile)
- if err != nil {
- t.Fatal(err)
- }
-
- test.Assert(t, tag, exp, got)
-}
-
func TestTrimNull(t *testing.T) {
type testCase struct {
in []byte
diff --git a/lib/dns/message_test.go b/lib/dns/message_test.go
index 9d833389..824ace68 100644
--- a/lib/dns/message_test.go
+++ b/lib/dns/message_test.go
@@ -1,6 +1,6 @@
-// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// SPDX-FileCopyrightText: 2018 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -9,7 +9,7 @@ import (
"encoding/json"
"testing"
- libbytes "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/hexdump"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
@@ -974,7 +974,7 @@ func TestMessageAddAuthority(t *testing.T) {
// Compare the message packet.
bb.Reset()
- libbytes.DumpPrettyTable(&bb, msg.Question.String(), msg.packet)
+ hexdump.PrettyPrint(&bb, msg.Question.String(), msg.packet)
exp = tdata.Output[`packet`]
test.Assert(t, `AddAuthority`, string(exp), bb.String())
@@ -2101,7 +2101,7 @@ func TestUnpackMessage_OPT(t *testing.T) {
bbuf bytes.Buffer
)
for _, tcase = range listCase {
- stream, err = libbytes.ParseHexDump(tdata.Input[tcase], true)
+ stream, err = hexdump.Parse(tdata.Input[tcase], true)
if err != nil {
t.Fatal(err)
}
@@ -2125,7 +2125,7 @@ func TestUnpackMessage_OPT(t *testing.T) {
t.Fatal(err)
}
bbuf.Reset()
- libbytes.DumpPrettyTable(&bbuf, msg.Question.String(), stream)
+ hexdump.PrettyPrint(&bbuf, msg.Question.String(), stream)
tcase += `.hexdump`
test.Assert(t, tcase, string(tdata.Output[tcase]), bbuf.String())
}
@@ -2161,7 +2161,7 @@ func TestUnpackMessage_SVCB(t *testing.T) {
msg *Message
)
for _, name = range listCase {
- stream, err = libbytes.ParseHexDump(tdata.Input[name], true)
+ stream, err = hexdump.Parse(tdata.Input[name], true)
if err != nil {
t.Fatal(logp, err)
}
diff --git a/lib/dns/zone_test.go b/lib/dns/zone_test.go
index d8b80814..b4f3feb1 100644
--- a/lib/dns/zone_test.go
+++ b/lib/dns/zone_test.go
@@ -1,6 +1,6 @@
-// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// SPDX-FileCopyrightText: 2018 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -10,7 +10,7 @@ import (
"strconv"
"testing"
- libbytes "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/hexdump"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
@@ -75,7 +75,7 @@ func TestParseZone(t *testing.T) {
)
for x, msg = range zone.messages {
bb.Reset()
- libbytes.DumpPrettyTable(&bb, msg.Question.String(), msg.packet)
+ hexdump.PrettyPrint(&bb, msg.Question.String(), msg.packet)
vstr = fmt.Sprintf(`message_%d.hex`, x)
vbytes = tdata.Output[vstr]
@@ -155,7 +155,7 @@ func TestParseZone_SVCB(t *testing.T) {
for x, msg = range zone.messages {
out.Reset()
- libbytes.DumpPrettyTable(&out, msg.Question.String(), msg.packet)
+ hexdump.PrettyPrint(&out, msg.Question.String(), msg.packet)
tag = fmt.Sprintf(`%s:message_%d.hex`, name, x)
stream = tdata.Output[tag]
diff --git a/lib/hexdump/hexdump.go b/lib/hexdump/hexdump.go
new file mode 100644
index 00000000..50c0a3ce
--- /dev/null
+++ b/lib/hexdump/hexdump.go
@@ -0,0 +1,217 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+// Package hexdump implements reading and writing bytes from and into
+// hexadecimal number.
+// It support parsing output from hexdump(1) tool.
+package hexdump
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
+)
+
+// Parse parse the default output of [hexdump(1)] utility from parameter in
+// back into stream of byte.
+//
+// An example of default output of hexdump is
+//
+// 0000000 7865 5f70 6964 2f72 0000 0000 0000 0000
+// 0000010 0000 0000 0000 0000 0000 0000 0000 0000
+// *
+// 0000060 0000 0000 3030 3030 3537 0035 3030 3130
+//
+// The first column is the address and the rest of the column is the data.
+//
+// Each data column is 16-bit words in little-endian order, so in the above
+// example, the first byte would be 65, second byte is 78 and so on.
+// If parameter networkByteOrder (big-endian) is true, the first byte would be
+// 78, second by is 65, and so on.
+//
+// The asterisk "*" means that the values from address 0000020 to 0000050 is
+// equal to the previous line, 0000010.
+//
+// [hexdump(1)]: https://man.archlinux.org/man/hexdump.1
+func Parse(in []byte, networkByteOrder bool) (out []byte, err error) {
+ var (
+ logp = `ParseHexDump`
+ parser = bytes.NewParser(in, []byte(" \n"))
+ d byte = 255 // Just to make the first for-loop pass.
+
+ token []byte
+ vint64 int64
+ x int
+ isAsterisk bool
+ )
+ for d != 0 {
+ // Read the address.
+ token, d = parser.Read()
+ if len(token) == 0 {
+ break
+ }
+ if len(token) == 1 {
+ if token[0] != '*' {
+ break
+ }
+ isAsterisk = true
+ continue
+ }
+
+ vint64, err = strconv.ParseInt(string(token), 16, 64)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ if isAsterisk {
+ if len(out) > 0 {
+ var start = len(out)
+ if start < 16 {
+ start = 0
+ } else {
+ start -= 16
+ }
+ var (
+ prevRow = out[start:]
+ identicalRow = int((vint64 - int64(len(out))) / 16)
+ )
+ for x = 0; x < identicalRow; x++ {
+ out = append(out, prevRow...)
+ }
+ }
+ }
+
+ // Read the two-hex, 16-bit words.
+ for x = 0; x < 8; x++ {
+ token, d = parser.Read()
+ if len(token) == 0 {
+ break
+ }
+
+ vint64, err = strconv.ParseInt(string(token), 16, 64)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ switch len(token) {
+ case 2:
+ out = append(out, byte(vint64))
+ case 4:
+ if networkByteOrder {
+ out = append(out, byte(vint64>>8))
+ out = append(out, byte(vint64))
+ } else {
+ out = append(out, byte(vint64))
+ out = append(out, byte(vint64>>8))
+ }
+ }
+
+ if d == '\n' {
+ break
+ }
+ }
+ // Ignore trailing characters.
+ if d != '\n' {
+ parser.SkipLine()
+ }
+ }
+ return out, nil
+}
+
+// Print print each byte in slice as hexadecimal value into N column length.
+func Print(title string, data []byte, col int) {
+ var (
+ start, x int
+ c byte
+ )
+ fmt.Print(title)
+ for x, c = range data {
+ if x%col == 0 {
+ if x > 0 {
+ fmt.Print(` ||`)
+ }
+ for y := start; y < x; y++ {
+ if data[y] >= 33 && data[y] <= 126 {
+ fmt.Printf(` %c`, data[y])
+ } else {
+ fmt.Print(` .`)
+ }
+ }
+ fmt.Printf("\n%4d -", x)
+ start = x
+ }
+
+ fmt.Printf(` %02X`, c)
+ }
+ rest := col - (x % col)
+ if rest > 0 {
+ for y := 1; y < rest; y++ {
+ fmt.Print(` `)
+ }
+ fmt.Print(` ||`)
+ }
+ for y := start; y <= x; y++ {
+ if data[y] >= 33 && data[y] <= 126 {
+ fmt.Printf(` %c`, data[y])
+ } else {
+ fmt.Print(` .`)
+ }
+ }
+
+ fmt.Println()
+}
+
+// PrettyPrint write each byte in slice data as hexadecimal, ASCII
+// character, and integer with 8 columns width.
+func PrettyPrint(w io.Writer, title string, data []byte) {
+ const ncol = 8
+
+ fmt.Fprintf(w, "%s\n", title)
+ fmt.Fprint(w, " | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |\n")
+ fmt.Fprint(w, " | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |\n")
+
+ var (
+ chunks = bytes.SplitEach(data, ncol)
+ chunk []byte
+ x int
+ y int
+ c byte
+ )
+ for x, chunk = range chunks {
+ fmt.Fprintf(w, `%#08x|`, x*ncol)
+
+ // Print as hex.
+ for y, c = range chunk {
+ fmt.Fprintf(w, ` %02x`, c)
+ }
+ for y++; y < ncol; y++ {
+ fmt.Fprint(w, ` `)
+ }
+
+ // Print as char.
+ fmt.Fprint(w, ` | `)
+ for y, c = range chunk {
+ if c >= 33 && c <= 126 {
+ fmt.Fprintf(w, `%c`, c)
+ } else {
+ fmt.Fprint(w, `.`)
+ }
+ }
+ for y++; y < ncol; y++ {
+ fmt.Fprint(w, ` `)
+ }
+
+ // Print as integer.
+ fmt.Fprint(w, ` |`)
+ for y, c = range chunk {
+ fmt.Fprintf(w, ` %3d`, c)
+ }
+ for y++; y < ncol; y++ {
+ fmt.Fprint(w, ` `)
+ }
+ fmt.Fprintf(w, " |%d\n", x*ncol)
+ }
+}
diff --git a/lib/hexdump/hexdump_example_test.go b/lib/hexdump/hexdump_example_test.go
new file mode 100644
index 00000000..24058ca2
--- /dev/null
+++ b/lib/hexdump/hexdump_example_test.go
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package hexdump_test
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+
+ libbytes "git.sr.ht/~shulhan/pakakeh.go/lib/bytes"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/hexdump"
+)
+
+func ExampleParse() {
+ var (
+ in = []byte(`0000000 6548 6c6c 2c6f 7720 726f 646c 0021`)
+
+ out []byte
+ err error
+ )
+
+ out, err = hexdump.Parse(in, false)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf(`%s`, libbytes.TrimNull(out))
+
+ // Output:
+ // Hello, world!
+}
+
+func ExamplePrettyPrint() {
+ var (
+ data = []byte{1, 2, 3, 'H', 'e', 'l', 'l', 'o', 254, 255}
+ bb bytes.Buffer
+ )
+
+ hexdump.PrettyPrint(&bb, `PrettyPrint`, data)
+ fmt.Println(bb.String())
+ // Output:
+ // PrettyPrint
+ // | 0 1 2 3 4 5 6 7 | 01234567 | 0 1 2 3 4 5 6 7 |
+ // | 8 9 A B C D E F | 89ABCDEF | 8 9 A B C D E F |
+ // 0x00000000| 01 02 03 48 65 6c 6c 6f | ...Hello | 1 2 3 72 101 108 108 111 |0
+ // 0x00000008| fe ff | .. | 254 255 |8
+}
+
+func ExamplePrint() {
+ title := `Print`
+ data := []byte(`Hello, world !`)
+ hexdump.Print(title, data, 5)
+
+ // Output:
+ // Print
+ // 0 - 48 65 6C 6C 6F || H e l l o
+ // 5 - 2C 20 77 6F 72 || , . w o r
+ // 10 - 6C 64 20 21 || l d . !
+}
diff --git a/lib/hexdump/hexdump_test.go b/lib/hexdump/hexdump_test.go
new file mode 100644
index 00000000..e1c21b8f
--- /dev/null
+++ b/lib/hexdump/hexdump_test.go
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package hexdump_test
+
+import (
+ "os"
+ "path"
+ "testing"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/hexdump"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/test"
+)
+
+func TestParse(t *testing.T) {
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/Parse_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ tag string
+ in []byte
+ exp []byte
+ got []byte
+ )
+ for tag, in = range tdata.Input {
+ exp = tdata.Output[tag]
+
+ got, err = hexdump.Parse(in, false)
+ if err != nil {
+ test.Assert(t, tag, string(exp), err.Error())
+ continue
+ }
+
+ test.Assert(t, tag, string(exp), string(got))
+ }
+}
+
+func TestParseExpDirTar(t *testing.T) {
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/Parse_exp_dir_tar_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ tag = `exp_dir.tar`
+ expFile = path.Join(`testdata`, tag)
+
+ exp []byte
+ got []byte
+ )
+
+ got, err = hexdump.Parse(tdata.Input[tag], false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exp, err = os.ReadFile(expFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, tag, exp, got)
+}
diff --git a/lib/bytes/testdata/ParseHexDump_exp_dir_tar_test.txt b/lib/hexdump/testdata/Parse_exp_dir_tar_test.txt
index dceff86f..0c50929e 100644
--- a/lib/bytes/testdata/ParseHexDump_exp_dir_tar_test.txt
+++ b/lib/hexdump/testdata/Parse_exp_dir_tar_test.txt
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
>>> exp_dir.tar
0000000 7865 5f70 6964 2f72 0000 0000 0000 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
diff --git a/lib/bytes/testdata/ParseHexDump_test.txt b/lib/hexdump/testdata/Parse_test.txt
index e326072d..72cbe110 100644
--- a/lib/bytes/testdata/ParseHexDump_test.txt
+++ b/lib/hexdump/testdata/Parse_test.txt
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
>>> trailing new line
0000000 6548 6c6c 2c6f 7720 726f 646c 0a21
diff --git a/lib/bytes/testdata/exp_dir.tar b/lib/hexdump/testdata/exp_dir.tar
index 465deda2..465deda2 100644
--- a/lib/bytes/testdata/exp_dir.tar
+++ b/lib/hexdump/testdata/exp_dir.tar
Binary files differ
diff --git a/lib/hexdump/testdata/exp_dir.tar.license b/lib/hexdump/testdata/exp_dir.tar.license
new file mode 100644
index 00000000..5dd7fe5c
--- /dev/null
+++ b/lib/hexdump/testdata/exp_dir.tar.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+
+SPDX-License-Identifier: BSD-3-Clause
diff --git a/lib/memfs/internal/test/embed/embed_test.go b/lib/memfs/internal/test/embed/embed_test.go
index 1dcfc954..06b16d3a 100644
--- a/lib/memfs/internal/test/embed/embed_test.go
+++ b/lib/memfs/internal/test/embed/embed_test.go
@@ -16,7 +16,7 @@ func generate_testdata() *memfs.Node {
GenFuncName: "generate_testdata",
}
node.SetMode(0o20000000755)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1699901114, 455911441)
node.SetName("/")
node.SetSize(0)
node.AddChild(_memFS_getNode(memFS, "/direct", generate_testdata_direct))
@@ -37,7 +37,7 @@ func generate_testdata_direct() *memfs.Node {
GenFuncName: "generate_testdata_direct",
}
node.SetMode(0o20000000755)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1569586540, 0)
node.SetName("direct")
node.SetSize(0)
node.AddChild(_memFS_getNode(memFS, "/direct/add", generate_testdata_direct_add))
@@ -52,7 +52,7 @@ func generate_testdata_direct_add() *memfs.Node {
GenFuncName: "generate_testdata_direct_add",
}
node.SetMode(0o20000000755)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1569586540, 0)
node.SetName("add")
node.SetSize(0)
node.AddChild(_memFS_getNode(memFS, "/direct/add/file", generate_testdata_direct_add_file))
@@ -69,7 +69,7 @@ func generate_testdata_direct_add_file() *memfs.Node {
Content: []byte("\x54\x65\x73\x74\x20\x64\x69\x72\x65\x63\x74\x20\x61\x64\x64\x20\x66\x69\x6C\x65\x2E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1569586540, 0)
node.SetName("file")
node.SetSize(22)
return node
@@ -84,7 +84,7 @@ func generate_testdata_direct_add_file2() *memfs.Node {
Content: []byte("\x54\x65\x73\x74\x20\x64\x69\x72\x65\x63\x74\x20\x61\x64\x64\x20\x66\x69\x6C\x65\x20\x32\x2E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1569586540, 0)
node.SetName("file2")
node.SetSize(24)
return node
@@ -98,7 +98,7 @@ func generate_testdata_exclude() *memfs.Node {
GenFuncName: "generate_testdata_exclude",
}
node.SetMode(0o20000000755)
- node.SetModTimeUnix(1710216708, 236975330)
+ node.SetModTimeUnix(1633792794, 467899662)
node.SetName("exclude")
node.SetSize(0)
node.AddChild(_memFS_getNode(memFS, "/exclude/dir", generate_testdata_exclude_dir))
@@ -116,7 +116,7 @@ func generate_testdata_exclude_dir() *memfs.Node {
GenFuncName: "generate_testdata_exclude_dir",
}
node.SetMode(0o20000000700)
- node.SetModTimeUnix(1710216708, 236975330)
+ node.SetModTimeUnix(1629620045, 798024232)
node.SetName("dir")
node.SetSize(0)
return node
@@ -131,7 +131,7 @@ func generate_testdata_exclude_index_link_css() *memfs.Node {
Content: []byte("\x62\x6F\x64\x79\x20\x7B\x0A\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index-link.css")
node.SetSize(9)
return node
@@ -146,7 +146,7 @@ func generate_testdata_exclude_index_link_html() *memfs.Node {
Content: []byte("\x3C\x68\x74\x6D\x6C\x3E\x3C\x2F\x68\x74\x6D\x6C\x3E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1588592347, 0)
node.SetName("index-link.html")
node.SetSize(14)
return node
@@ -161,7 +161,7 @@ func generate_testdata_exclude_index_link_js() *memfs.Node {
Content: []byte("\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x58\x28\x29\x20\x7B\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index-link.js")
node.SetSize(16)
return node
@@ -175,7 +175,7 @@ func generate_testdata_include() *memfs.Node {
GenFuncName: "generate_testdata_include",
}
node.SetMode(0o20000000755)
- node.SetModTimeUnix(1710216708, 236975330)
+ node.SetModTimeUnix(1629623653, 749800911)
node.SetName("include")
node.SetSize(0)
node.AddChild(_memFS_getNode(memFS, "/include/dir", generate_testdata_include_dir))
@@ -193,7 +193,7 @@ func generate_testdata_include_dir() *memfs.Node {
GenFuncName: "generate_testdata_include_dir",
}
node.SetMode(0o20000000700)
- node.SetModTimeUnix(1710216708, 236975330)
+ node.SetModTimeUnix(1564836597, 0)
node.SetName("dir")
node.SetSize(0)
return node
@@ -208,7 +208,7 @@ func generate_testdata_include_index_css() *memfs.Node {
Content: []byte("\x62\x6F\x64\x79\x20\x7B\x0A\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index.css")
node.SetSize(9)
return node
@@ -223,7 +223,7 @@ func generate_testdata_include_index_html() *memfs.Node {
Content: []byte("\x3C\x68\x74\x6D\x6C\x3E\x3C\x2F\x68\x74\x6D\x6C\x3E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1588592347, 0)
node.SetName("index.html")
node.SetSize(14)
return node
@@ -238,7 +238,7 @@ func generate_testdata_include_index_js() *memfs.Node {
Content: []byte("\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x58\x28\x29\x20\x7B\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index.js")
node.SetSize(16)
return node
@@ -253,7 +253,7 @@ func generate_testdata_index_css() *memfs.Node {
Content: []byte("\x62\x6F\x64\x79\x20\x7B\x0A\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 626038828)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index.css")
node.SetSize(9)
return node
@@ -268,7 +268,7 @@ func generate_testdata_index_html() *memfs.Node {
Content: []byte("\x3C\x68\x74\x6D\x6C\x3E\x3C\x2F\x68\x74\x6D\x6C\x3E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1588592347, 0)
node.SetName("index.html")
node.SetSize(14)
return node
@@ -283,7 +283,7 @@ func generate_testdata_index_js() *memfs.Node {
Content: []byte("\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x58\x28\x29\x20\x7B\x7D\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("index.js")
node.SetSize(16)
return node
@@ -298,7 +298,7 @@ func generate_testdata_plain() *memfs.Node {
Content: []byte("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x70\x6C\x61\x69\x6E\x20\x74\x65\x78\x74\x2E\x0A"),
}
node.SetMode(0o644)
- node.SetModTimeUnix(1709751019, 629372161)
+ node.SetModTimeUnix(1562038157, 0)
node.SetName("plain")
node.SetSize(22)
return node