diff --git a/go.mod b/go.mod index 635140c4d6..2da25b8957 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,7 @@ require ( github.com/nats-io/nats-server/v2 v2.12.4 github.com/nats-io/nats.go v1.49.0 github.com/oklog/run v1.2.0 - github.com/olekukonko/tablewriter v1.1.3 + github.com/olekukonko/tablewriter v1.1.4 github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 @@ -166,9 +166,8 @@ require ( github.com/ceph/go-ceph v0.37.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect - github.com/clipperhouse/displaywidth v0.6.2 // indirect - github.com/clipperhouse/stringish v0.1.1 // indirect - github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/clipperhouse/displaywidth v0.10.0 // indirect + github.com/clipperhouse/uax29/v2 v2.6.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -311,8 +310,8 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect - github.com/olekukonko/errors v1.1.0 // indirect - github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 // indirect + github.com/olekukonko/errors v1.2.0 // indirect + github.com/olekukonko/ll v0.1.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect diff --git a/go.sum b/go.sum index de2c5aebc2..bc0ea8b693 100644 --- a/go.sum +++ b/go.sum @@ -221,12 +221,10 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo= -github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= -github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= +github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= +github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= +github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= @@ -934,13 +932,13 @@ github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YF github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= -github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= -github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 h1:jrYnow5+hy3WRDCBypUFvVKNSPPCdqgSXIE9eJDD8LM= -github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew= +github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= +github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= +github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA= -github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM= +github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= +github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= diff --git a/vendor/github.com/clipperhouse/displaywidth/.gitignore b/vendor/github.com/clipperhouse/displaywidth/.gitignore index e43b0f9889..b356d43c6f 100644 --- a/vendor/github.com/clipperhouse/displaywidth/.gitignore +++ b/vendor/github.com/clipperhouse/displaywidth/.gitignore @@ -1 +1,3 @@ .DS_Store +*.out +*.test diff --git a/vendor/github.com/clipperhouse/displaywidth/AGENTS.md b/vendor/github.com/clipperhouse/displaywidth/AGENTS.md index 853e2917db..ce8d7c0334 100644 --- a/vendor/github.com/clipperhouse/displaywidth/AGENTS.md +++ b/vendor/github.com/clipperhouse/displaywidth/AGENTS.md @@ -18,13 +18,15 @@ by running `go generate` from the top package directory. ## Pull Requests and branches -For PRs (pull requests), you can use the gh CLI tool to retrieve details, -or post comments. Then, compare the current branch with main. Reviewing a PR -and reviewing a branch are about the same, but the PR may add context. +For PRs (pull requests), you can use the gh CLI tool. Compare the current branch with main. Reviewing a PR and reviewing a branch are about the same, but the PR may add context. -Look for bugs. Think like GitHub Copilot or Cursor BugBot. +Understand the goals of the PR. Note any API changes, especially breaking changes. -Offer to post a brief summary of the review to the PR, via the gh CLI tool. +Look for thoroughness of tests, as well as GoDoc comments. + +Retrieve and consider the comments on the PR, which may have come from GitHub Copilot or Cursor BugBot. Think like GitHub Copilot or Cursor BugBot. + +Offer to optionally post a brief summary of the review to the PR, via the gh CLI tool. ## Comparisons to go-runewidth diff --git a/vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md b/vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md index c97ce3b813..c08c89908f 100644 --- a/vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md +++ b/vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md @@ -1,5 +1,50 @@ # Changelog +## [0.10.0] + +[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.9.0...v0.10.0) + +### Added +- New `ControlSequences` option to treat ECMA-48/ANSI escape sequences as zero-width. (#20) +- `TruncateString` and `TruncateBytes` now preserve trailing ANSI escape sequences (such as SGR resets) when `ControlSequences` is true, preventing color bleed in terminal output. + +### Changed +- Removed `stringish` dependency; generic type constraints are now inline `~string | []byte`. +- Upgraded uax29 dependency to v2.6.0 for ANSI escape sequence support in the grapheme iterator. + +## [0.9.0] + +[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.8.0...v0.9.0) + +### Changed +- Unicode 17 support: East Asian Width and emoji data updated to Unicode 17.0.0. (#18) +- Upgraded uax29 dependency to v2.5.0 (Unicode 17 grapheme segmentation). + +## [0.8.0] + +[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.7.0...v0.8.0) + +### Changed +- Performance: ASCII fast path that applies to any run of printable + ASCII. 2x-10x faster for ASCII text vs v0.7.0. (#16) +- Upgraded uax29 dependency to v2.4.0 for Unicode 16 support. Text that includes + Indic_Conjunct_Break may segment differently (and more correctly). (#15) + +## [0.7.0] + +[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.2...v0.7.0) + +### Added +- New `TruncateString` and `TruncateBytes` methods to truncate strings to a + maximum display width, with optional tail (like an ellipsis). (#13) + +## [0.6.2] + +[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.1...v0.6.2) + +### Changed +- Internal: reduced property categories for simpler trie. + ## [0.6.1] [Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1) @@ -19,7 +64,7 @@ widths of grapheme clusters. ### Changed -- Added ASCII fast paths +- Fast ASCII lookups ## [0.5.0] diff --git a/vendor/github.com/clipperhouse/displaywidth/README.md b/vendor/github.com/clipperhouse/displaywidth/README.md index e9a513cae9..17a3981fce 100644 --- a/vendor/github.com/clipperhouse/displaywidth/README.md +++ b/vendor/github.com/clipperhouse/displaywidth/README.md @@ -61,8 +61,28 @@ func main() { ### Options -There is one option, `displaywidth.Options.EastAsianWidth`, which defines -how [East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous) +Create the options you need, and then use methods on the options struct. + +```go +var myOptions = displaywidth.Options{ + EastAsianWidth: true, + ControlSequences: true, +} + +width := myOptions.String("Hello, 世界!") +``` + +#### ControlSequences + +`ControlSequences` specifies whether to ignore ECMA-48 escape sequences +when calculating the display width. When `false` (default), ANSI escape +sequences are treated as just a series of characters. When `true`, they are +treated as a single zero-width unit. + +#### EastAsianWidth + +`EastAsianWidth` defines how +[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous) are treated. When `false` (default), East Asian Ambiguous characters are treated as width 1. @@ -70,26 +90,8 @@ When `true`, they are treated as width 2. You may wish to configure this based on environment variables or locale. `go-runewidth`, for example, does so - [during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2). + [during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2). `displaywidth` does not do this automatically, we prefer to leave it to you. -`displaywidth` does not do this automatically, we prefer to leave it to you. -You might do something like: - -```go -var width displaywidth.Options // zero value is default - -func init() { - if os.Getenv("EAST_ASIAN_WIDTH") == "true" { - width = displaywidth.Options{EastAsianWidth: true} - } - // or check locale, or any other logic you want -} - -// use it in your logic -func myApp() { - fmt.Println(width.String("Hello, 世界!")) -} -``` ## Technical standards and compatibility @@ -97,19 +99,16 @@ This package implements the Unicode East Asian Width standard ([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles [version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)), and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol) -(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html). We are keeping -an eye on [emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/). +(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html) +for emojis. We are keeping an eye on +[emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/). +For control sequences, we implement the [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) standard for 7-bit ASCII control sequences. `clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will give the same outputs for most real-world text. Extensive details are in the [compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md). -If you wish to investigate the core logic, see the `lookupProperties` and `width` -functions in [width.go](width.go#L139). The essential trie generation logic is in -`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L316). - - ## Prior Art [mattn/go-runewidth](https://github.com/mattn/go-runewidth) @@ -133,33 +132,39 @@ goarch: arm64 pkg: github.com/clipperhouse/displaywidth/comparison cpu: Apple M2 -BenchmarkString_Mixed/clipperhouse/displaywidth-8 10326 ns/op 163.37 MB/s 0 B/op 0 allocs/op -BenchmarkString_Mixed/mattn/go-runewidth-8 14415 ns/op 117.03 MB/s 0 B/op 0 allocs/op -BenchmarkString_Mixed/rivo/uniseg-8 19343 ns/op 87.21 MB/s 0 B/op 0 allocs/op +BenchmarkString_Mixed/clipperhouse/displaywidth-8 5784 ns/op 291.69 MB/s 0 B/op 0 allocs/op +BenchmarkString_Mixed/mattn/go-runewidth-8 14751 ns/op 114.36 MB/s 0 B/op 0 allocs/op +BenchmarkString_Mixed/rivo/uniseg-8 19360 ns/op 87.14 MB/s 0 B/op 0 allocs/op -BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10561 ns/op 159.74 MB/s 0 B/op 0 allocs/op -BenchmarkString_EastAsian/mattn/go-runewidth-8 23790 ns/op 70.91 MB/s 0 B/op 0 allocs/op -BenchmarkString_EastAsian/rivo/uniseg-8 19322 ns/op 87.31 MB/s 0 B/op 0 allocs/op +BenchmarkString_ASCII/clipperhouse/displaywidth-8 54.60 ns/op 2344.32 MB/s 0 B/op 0 allocs/op +BenchmarkString_ASCII/mattn/go-runewidth-8 1195 ns/op 107.08 MB/s 0 B/op 0 allocs/op +BenchmarkString_ASCII/rivo/uniseg-8 1578 ns/op 81.13 MB/s 0 B/op 0 allocs/op -BenchmarkString_ASCII/clipperhouse/displaywidth-8 1033 ns/op 123.88 MB/s 0 B/op 0 allocs/op -BenchmarkString_ASCII/mattn/go-runewidth-8 1168 ns/op 109.59 MB/s 0 B/op 0 allocs/op -BenchmarkString_ASCII/rivo/uniseg-8 1585 ns/op 80.74 MB/s 0 B/op 0 allocs/op +BenchmarkString_EastAsian/clipperhouse/displaywidth-8 5837 ns/op 289.01 MB/s 0 B/op 0 allocs/op +BenchmarkString_EastAsian/mattn/go-runewidth-8 24418 ns/op 69.09 MB/s 0 B/op 0 allocs/op +BenchmarkString_EastAsian/rivo/uniseg-8 19339 ns/op 87.23 MB/s 0 B/op 0 allocs/op -BenchmarkString_Emoji/clipperhouse/displaywidth-8 3034 ns/op 238.61 MB/s 0 B/op 0 allocs/op -BenchmarkString_Emoji/mattn/go-runewidth-8 4797 ns/op 150.94 MB/s 0 B/op 0 allocs/op -BenchmarkString_Emoji/rivo/uniseg-8 6612 ns/op 109.50 MB/s 0 B/op 0 allocs/op +BenchmarkString_Emoji/clipperhouse/displaywidth-8 3225 ns/op 224.51 MB/s 0 B/op 0 allocs/op +BenchmarkString_Emoji/mattn/go-runewidth-8 4851 ns/op 149.25 MB/s 0 B/op 0 allocs/op +BenchmarkString_Emoji/rivo/uniseg-8 6591 ns/op 109.85 MB/s 0 B/op 0 allocs/op -BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3343 ns/op 504.67 MB/s 0 B/op 0 allocs/op -BenchmarkRune_Mixed/mattn/go-runewidth-8 5414 ns/op 311.62 MB/s 0 B/op 0 allocs/op +BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3385 ns/op 498.34 MB/s 0 B/op 0 allocs/op +BenchmarkRune_Mixed/mattn/go-runewidth-8 5354 ns/op 315.07 MB/s 0 B/op 0 allocs/op -BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3393 ns/op 497.17 MB/s 0 B/op 0 allocs/op -BenchmarkRune_EastAsian/mattn/go-runewidth-8 15312 ns/op 110.17 MB/s 0 B/op 0 allocs/op +BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3397 ns/op 496.56 MB/s 0 B/op 0 allocs/op +BenchmarkRune_EastAsian/mattn/go-runewidth-8 15673 ns/op 107.64 MB/s 0 B/op 0 allocs/op -BenchmarkRune_ASCII/clipperhouse/displaywidth-8 256.9 ns/op 498.32 MB/s 0 B/op 0 allocs/op -BenchmarkRune_ASCII/mattn/go-runewidth-8 265.7 ns/op 481.75 MB/s 0 B/op 0 allocs/op +BenchmarkRune_ASCII/clipperhouse/displaywidth-8 255.7 ns/op 500.53 MB/s 0 B/op 0 allocs/op +BenchmarkRune_ASCII/mattn/go-runewidth-8 261.5 ns/op 489.55 MB/s 0 B/op 0 allocs/op -BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1336 ns/op 541.96 MB/s 0 B/op 0 allocs/op -BenchmarkRune_Emoji/mattn/go-runewidth-8 2304 ns/op 314.23 MB/s 0 B/op 0 allocs/op +BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1371 ns/op 528.22 MB/s 0 B/op 0 allocs/op +BenchmarkRune_Emoji/mattn/go-runewidth-8 2267 ns/op 319.43 MB/s 0 B/op 0 allocs/op + +BenchmarkTruncateWithTail/clipperhouse/displaywidth-8 3229 ns/op 54.82 MB/s 192 B/op 14 allocs/op +BenchmarkTruncateWithTail/mattn/go-runewidth-8 8408 ns/op 21.05 MB/s 192 B/op 14 allocs/op + +BenchmarkTruncateWithoutTail/clipperhouse/displaywidth-8 3554 ns/op 64.43 MB/s 0 B/op 0 allocs/op +BenchmarkTruncateWithoutTail/mattn/go-runewidth-8 11189 ns/op 20.47 MB/s 0 B/op 0 allocs/op ``` Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/). diff --git a/vendor/github.com/clipperhouse/displaywidth/graphemes.go b/vendor/github.com/clipperhouse/displaywidth/graphemes.go index 673c2aab50..2d70c46b30 100644 --- a/vendor/github.com/clipperhouse/displaywidth/graphemes.go +++ b/vendor/github.com/clipperhouse/displaywidth/graphemes.go @@ -1,7 +1,6 @@ package displaywidth import ( - "github.com/clipperhouse/stringish" "github.com/clipperhouse/uax29/v2/graphemes" ) @@ -9,8 +8,8 @@ import ( // // Iterate using the Next method, and get the width of the current grapheme // using the Width method. -type Graphemes[T stringish.Interface] struct { - iter graphemes.Iterator[T] +type Graphemes[T ~string | []byte] struct { + iter *graphemes.Iterator[T] options Options } @@ -44,10 +43,10 @@ func StringGraphemes(s string) Graphemes[string] { // Iterate using the Next method, and get the width of the current grapheme // using the Width method. func (options Options) StringGraphemes(s string) Graphemes[string] { - return Graphemes[string]{ - iter: graphemes.FromString(s), - options: options, - } + g := graphemes.FromString(s) + g.AnsiEscapeSequences = options.ControlSequences + + return Graphemes[string]{iter: g, options: options} } // BytesGraphemes returns an iterator over grapheme clusters for the given @@ -65,8 +64,8 @@ func BytesGraphemes(s []byte) Graphemes[[]byte] { // Iterate using the Next method, and get the width of the current grapheme // using the Width method. func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] { - return Graphemes[[]byte]{ - iter: graphemes.FromBytes(s), - options: options, - } + g := graphemes.FromBytes(s) + g.AnsiEscapeSequences = options.ControlSequences + + return Graphemes[[]byte]{iter: g, options: options} } diff --git a/vendor/github.com/clipperhouse/displaywidth/trie.go b/vendor/github.com/clipperhouse/displaywidth/trie.go index b03f882811..1d3a983005 100644 --- a/vendor/github.com/clipperhouse/displaywidth/trie.go +++ b/vendor/github.com/clipperhouse/displaywidth/trie.go @@ -2,8 +2,6 @@ package displaywidth -import "github.com/clipperhouse/stringish" - // property is an enum representing the properties of a character type property uint8 @@ -19,7 +17,7 @@ const ( // lookup returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. -func lookup[T stringish.Interface](s T) (v uint8, sz int) { +func lookup[T ~string | []byte](s T) (v uint8, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII @@ -79,7 +77,7 @@ func lookup[T stringish.Interface](s T) (v uint8, sz int) { return 0, 1 } -// stringWidthTrie. Total size: 17664 bytes (17.25 KiB). Checksum: c77d82ff2d69f0d2. +// stringWidthTrie. Total size: 17664 bytes (17.25 KiB). Checksum: 220983462f26d765. // type stringWidthTrie struct { } // func newStringWidthTrie(i int) *stringWidthTrie { @@ -1133,27 +1131,31 @@ var stringWidthValues = [15744]uint8{ // Block 0xc2, offset 0x3080 0x30a0: 0x0002, 0x30a1: 0x0002, 0x30a2: 0x0002, 0x30a3: 0x0002, 0x30a4: 0x0001, - 0x30b0: 0x0002, 0x30b1: 0x0002, + 0x30b0: 0x0002, 0x30b1: 0x0002, 0x30b2: 0x0002, 0x30b3: 0x0002, 0x30b4: 0x0002, 0x30b5: 0x0002, + 0x30b6: 0x0002, // Block 0xc3, offset 0x30c0 0x30c0: 0x0002, 0x30c1: 0x0002, 0x30c2: 0x0002, 0x30c3: 0x0002, 0x30c4: 0x0002, 0x30c5: 0x0002, 0x30c6: 0x0002, 0x30c7: 0x0002, 0x30c8: 0x0002, 0x30c9: 0x0002, 0x30ca: 0x0002, 0x30cb: 0x0002, 0x30cc: 0x0002, 0x30cd: 0x0002, 0x30ce: 0x0002, 0x30cf: 0x0002, 0x30d0: 0x0002, 0x30d1: 0x0002, - 0x30d2: 0x0002, 0x30d3: 0x0002, 0x30d4: 0x0002, 0x30d5: 0x0002, 0x30d6: 0x0002, 0x30d7: 0x0002, - 0x30d8: 0x0002, 0x30d9: 0x0002, 0x30da: 0x0002, 0x30db: 0x0002, 0x30dc: 0x0002, 0x30dd: 0x0002, - 0x30de: 0x0002, 0x30df: 0x0002, 0x30e0: 0x0002, 0x30e1: 0x0002, 0x30e2: 0x0002, 0x30e3: 0x0002, - 0x30e4: 0x0002, 0x30e5: 0x0002, 0x30e6: 0x0002, 0x30e7: 0x0002, 0x30e8: 0x0002, 0x30e9: 0x0002, - 0x30ea: 0x0002, 0x30eb: 0x0002, 0x30ec: 0x0002, 0x30ed: 0x0002, 0x30ee: 0x0002, 0x30ef: 0x0002, - 0x30f0: 0x0002, 0x30f1: 0x0002, 0x30f2: 0x0002, 0x30f3: 0x0002, 0x30f4: 0x0002, 0x30f5: 0x0002, - 0x30f6: 0x0002, 0x30f7: 0x0002, + 0x30d2: 0x0002, 0x30d3: 0x0002, 0x30d4: 0x0002, 0x30d5: 0x0002, + 0x30ff: 0x0002, // Block 0xc4, offset 0x3100 0x3100: 0x0002, 0x3101: 0x0002, 0x3102: 0x0002, 0x3103: 0x0002, 0x3104: 0x0002, 0x3105: 0x0002, 0x3106: 0x0002, 0x3107: 0x0002, 0x3108: 0x0002, 0x3109: 0x0002, 0x310a: 0x0002, 0x310b: 0x0002, 0x310c: 0x0002, 0x310d: 0x0002, 0x310e: 0x0002, 0x310f: 0x0002, 0x3110: 0x0002, 0x3111: 0x0002, - 0x3112: 0x0002, 0x3113: 0x0002, 0x3114: 0x0002, 0x3115: 0x0002, - 0x313f: 0x0002, + 0x3112: 0x0002, 0x3113: 0x0002, 0x3114: 0x0002, 0x3115: 0x0002, 0x3116: 0x0002, 0x3117: 0x0002, + 0x3118: 0x0002, 0x3119: 0x0002, 0x311a: 0x0002, 0x311b: 0x0002, 0x311c: 0x0002, 0x311d: 0x0002, + 0x311e: 0x0002, // Block 0xc5, offset 0x3140 0x3140: 0x0002, 0x3141: 0x0002, 0x3142: 0x0002, 0x3143: 0x0002, 0x3144: 0x0002, 0x3145: 0x0002, - 0x3146: 0x0002, 0x3147: 0x0002, 0x3148: 0x0002, + 0x3146: 0x0002, 0x3147: 0x0002, 0x3148: 0x0002, 0x3149: 0x0002, 0x314a: 0x0002, 0x314b: 0x0002, + 0x314c: 0x0002, 0x314d: 0x0002, 0x314e: 0x0002, 0x314f: 0x0002, 0x3150: 0x0002, 0x3151: 0x0002, + 0x3152: 0x0002, 0x3153: 0x0002, 0x3154: 0x0002, 0x3155: 0x0002, 0x3156: 0x0002, 0x3157: 0x0002, + 0x3158: 0x0002, 0x3159: 0x0002, 0x315a: 0x0002, 0x315b: 0x0002, 0x315c: 0x0002, 0x315d: 0x0002, + 0x315e: 0x0002, 0x315f: 0x0002, 0x3160: 0x0002, 0x3161: 0x0002, 0x3162: 0x0002, 0x3163: 0x0002, + 0x3164: 0x0002, 0x3165: 0x0002, 0x3166: 0x0002, 0x3167: 0x0002, 0x3168: 0x0002, 0x3169: 0x0002, + 0x316a: 0x0002, 0x316b: 0x0002, 0x316c: 0x0002, 0x316d: 0x0002, 0x316e: 0x0002, 0x316f: 0x0002, + 0x3170: 0x0002, 0x3171: 0x0002, 0x3172: 0x0002, // Block 0xc6, offset 0x3180 0x31b0: 0x0002, 0x31b1: 0x0002, 0x31b2: 0x0002, 0x31b3: 0x0002, 0x31b5: 0x0002, 0x31b6: 0x0002, 0x31b7: 0x0002, 0x31b8: 0x0002, 0x31b9: 0x0002, 0x31ba: 0x0002, 0x31bb: 0x0002, @@ -1443,7 +1445,7 @@ var stringWidthValues = [15744]uint8{ 0x3b40: 0x0002, 0x3b41: 0x0002, 0x3b42: 0x0002, 0x3b43: 0x0002, 0x3b44: 0x0002, 0x3b45: 0x0002, 0x3b4c: 0x0002, 0x3b50: 0x0002, 0x3b51: 0x0002, 0x3b52: 0x0002, 0x3b55: 0x0002, 0x3b56: 0x0002, 0x3b57: 0x0002, - 0x3b5c: 0x0002, 0x3b5d: 0x0002, + 0x3b58: 0x0002, 0x3b5c: 0x0002, 0x3b5d: 0x0002, 0x3b5e: 0x0002, 0x3b5f: 0x0002, 0x3b6b: 0x0002, 0x3b6c: 0x0002, 0x3b74: 0x0002, 0x3b75: 0x0002, @@ -1482,8 +1484,8 @@ var stringWidthValues = [15744]uint8{ 0x3c7c: 0x0002, // Block 0xf2, offset 0x3c80 0x3c80: 0x0002, 0x3c81: 0x0002, 0x3c82: 0x0002, 0x3c83: 0x0002, 0x3c84: 0x0002, 0x3c85: 0x0002, - 0x3c86: 0x0002, 0x3c87: 0x0002, 0x3c88: 0x0002, 0x3c89: 0x0002, - 0x3c8f: 0x0002, 0x3c90: 0x0002, 0x3c91: 0x0002, + 0x3c86: 0x0002, 0x3c87: 0x0002, 0x3c88: 0x0002, 0x3c89: 0x0002, 0x3c8a: 0x0002, + 0x3c8e: 0x0002, 0x3c8f: 0x0002, 0x3c90: 0x0002, 0x3c91: 0x0002, 0x3c92: 0x0002, 0x3c93: 0x0002, 0x3c94: 0x0002, 0x3c95: 0x0002, 0x3c96: 0x0002, 0x3c97: 0x0002, 0x3c98: 0x0002, 0x3c99: 0x0002, 0x3c9a: 0x0002, 0x3c9b: 0x0002, 0x3c9c: 0x0002, 0x3c9d: 0x0002, 0x3c9e: 0x0002, 0x3c9f: 0x0002, 0x3ca0: 0x0002, 0x3ca1: 0x0002, 0x3ca2: 0x0002, 0x3ca3: 0x0002, @@ -1494,12 +1496,13 @@ var stringWidthValues = [15744]uint8{ 0x3cbc: 0x0002, 0x3cbd: 0x0002, 0x3cbe: 0x0002, 0x3cbf: 0x0002, // Block 0xf3, offset 0x3cc0 0x3cc0: 0x0002, 0x3cc1: 0x0002, 0x3cc2: 0x0002, 0x3cc3: 0x0002, 0x3cc4: 0x0002, 0x3cc5: 0x0002, - 0x3cc6: 0x0002, - 0x3cce: 0x0002, 0x3ccf: 0x0002, 0x3cd0: 0x0002, 0x3cd1: 0x0002, + 0x3cc6: 0x0002, 0x3cc8: 0x0002, + 0x3ccd: 0x0002, 0x3cce: 0x0002, 0x3ccf: 0x0002, 0x3cd0: 0x0002, 0x3cd1: 0x0002, 0x3cd2: 0x0002, 0x3cd3: 0x0002, 0x3cd4: 0x0002, 0x3cd5: 0x0002, 0x3cd6: 0x0002, 0x3cd7: 0x0002, 0x3cd8: 0x0002, 0x3cd9: 0x0002, 0x3cda: 0x0002, 0x3cdb: 0x0002, 0x3cdc: 0x0002, 0x3cdf: 0x0002, 0x3ce0: 0x0002, 0x3ce1: 0x0002, 0x3ce2: 0x0002, 0x3ce3: 0x0002, 0x3ce4: 0x0002, 0x3ce5: 0x0002, 0x3ce6: 0x0002, 0x3ce7: 0x0002, 0x3ce8: 0x0002, 0x3ce9: 0x0002, + 0x3cea: 0x0002, 0x3cef: 0x0002, 0x3cf0: 0x0002, 0x3cf1: 0x0002, 0x3cf2: 0x0002, 0x3cf3: 0x0002, 0x3cf4: 0x0002, 0x3cf5: 0x0002, 0x3cf6: 0x0002, 0x3cf7: 0x0002, 0x3cf8: 0x0002, // Block 0xf4, offset 0x3d00 @@ -1631,10 +1634,10 @@ var stringWidthIndex = [1920]uint8{ 0x440: 0x39, 0x441: 0x39, 0x442: 0x39, 0x443: 0x39, 0x444: 0x39, 0x445: 0x39, 0x446: 0x39, 0x447: 0x39, 0x448: 0x39, 0x449: 0x39, 0x44a: 0x39, 0x44b: 0x39, 0x44c: 0x39, 0x44d: 0x39, 0x44e: 0x39, 0x44f: 0x39, 0x450: 0x39, 0x451: 0x39, 0x452: 0x39, 0x453: 0x39, 0x454: 0x39, 0x455: 0x39, 0x456: 0x39, 0x457: 0x39, - 0x458: 0x39, 0x459: 0x39, 0x45a: 0x39, 0x45b: 0x39, 0x45c: 0x39, 0x45d: 0x39, 0x45e: 0x39, 0x45f: 0xc1, + 0x458: 0x39, 0x459: 0x39, 0x45a: 0x39, 0x45b: 0x39, 0x45c: 0x39, 0x45d: 0x39, 0x45e: 0x39, 0x45f: 0x39, 0x460: 0x39, 0x461: 0x39, 0x462: 0x39, 0x463: 0x39, 0x464: 0x39, 0x465: 0x39, 0x466: 0x39, 0x467: 0x39, 0x468: 0x39, 0x469: 0x39, 0x46a: 0x39, 0x46b: 0x39, 0x46c: 0x39, 0x46d: 0x39, 0x46e: 0x39, 0x46f: 0x39, - 0x470: 0x39, 0x471: 0x39, 0x472: 0x39, 0x473: 0xc2, 0x474: 0xc3, + 0x470: 0x39, 0x471: 0x39, 0x472: 0x39, 0x473: 0xc1, 0x474: 0xc2, 0x476: 0x39, 0x477: 0xc3, // Block 0x12, offset 0x480 0x4bf: 0xc4, // Block 0x13, offset 0x4c0 diff --git a/vendor/github.com/clipperhouse/displaywidth/width.go b/vendor/github.com/clipperhouse/displaywidth/width.go index a96ab0b075..8c183aa9d3 100644 --- a/vendor/github.com/clipperhouse/displaywidth/width.go +++ b/vendor/github.com/clipperhouse/displaywidth/width.go @@ -1,23 +1,34 @@ package displaywidth import ( + "strings" "unicode/utf8" - "github.com/clipperhouse/stringish" "github.com/clipperhouse/uax29/v2/graphemes" ) // Options allows you to specify the treatment of ambiguous East Asian -// characters. When EastAsianWidth is false (default), ambiguous East Asian -// characters are treated as width 1. When EastAsianWidth is true, ambiguous -// East Asian characters are treated as width 2. +// characters and ANSI escape sequences. type Options struct { + // EastAsianWidth specifies whether to treat ambiguous East Asian characters + // as width 1 or 2. When false (default), ambiguous East Asian characters + // are treated as width 1. When true, they are width 2. EastAsianWidth bool + + // ControlSequences specifies whether to ignore ECMA-48 escape sequences + // when calculating the display width. When false (default), ANSI escape + // sequences are treated as just a series of characters. When true, they are + // treated as a single zero-width unit. + // + // Note that this option is about *sequences*. Individual control characters + // are already treated as zero-width. With this option, ANSI sequences such as + // "\x1b[31m" and "\x1b[0m" do not count towards the width of a string. + ControlSequences bool } // DefaultOptions is the default options for the display width -// calculation, which is EastAsianWidth: false. -var DefaultOptions = Options{EastAsianWidth: false} +// calculation, which is EastAsianWidth false and ControlSequences false. +var DefaultOptions = Options{EastAsianWidth: false, ControlSequences: false} // String calculates the display width of a string, // by iterating over grapheme clusters in the string @@ -29,19 +40,43 @@ func String(s string) int { // String calculates the display width of a string, for the given options, by // iterating over grapheme clusters in the string and summing their widths. func (options Options) String(s string) int { - // Optimization: no need to parse grapheme - switch len(s) { - case 0: - return 0 - case 1: - return asciiWidth(s[0]) + width := 0 + pos := 0 + + for pos < len(s) { + // Try ASCII optimization + asciiLen := printableASCIILength(s[pos:]) + if asciiLen > 0 { + width += asciiLen + pos += asciiLen + continue + } + + // Not ASCII, use grapheme parsing + g := graphemes.FromString(s[pos:]) + g.AnsiEscapeSequences = options.ControlSequences + + start := pos + + for g.Next() { + v := g.Value() + width += graphemeWidth(v, options) + pos += len(v) + + // Quick check: if remaining might have printable ASCII, break to outer loop + if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E { + break + } + } + + // Defensive, should not happen: if no progress was made, + // skip a byte to prevent infinite loop. Only applies if + // the grapheme parser misbehaves. + if pos == start { + pos++ + } } - width := 0 - g := graphemes.FromString(s) - for g.Next() { - width += graphemeWidth(g.Value(), options) - } return width } @@ -55,19 +90,43 @@ func Bytes(s []byte) int { // Bytes calculates the display width of a []byte, for the given options, by // iterating over grapheme clusters in the slice and summing their widths. func (options Options) Bytes(s []byte) int { - // Optimization: no need to parse grapheme - switch len(s) { - case 0: - return 0 - case 1: - return asciiWidth(s[0]) + width := 0 + pos := 0 + + for pos < len(s) { + // Try ASCII optimization + asciiLen := printableASCIILength(s[pos:]) + if asciiLen > 0 { + width += asciiLen + pos += asciiLen + continue + } + + // Not ASCII, use grapheme parsing + g := graphemes.FromBytes(s[pos:]) + g.AnsiEscapeSequences = options.ControlSequences + + start := pos + + for g.Next() { + v := g.Value() + width += graphemeWidth(v, options) + pos += len(v) + + // Quick check: if remaining might have printable ASCII, break to outer loop + if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E { + break + } + } + + // Defensive, should not happen: if no progress was made, + // skip a byte to prevent infinite loop. Only applies if + // the grapheme parser misbehaves. + if pos == start { + pos++ + } } - width := 0 - g := graphemes.FromBytes(s) - for g.Next() { - width += graphemeWidth(g.Value(), options) - } return width } @@ -107,9 +166,123 @@ func (options Options) Rune(r rune) int { const _Default property = 0 +// TruncateString truncates a string to the given maxWidth, and appends the +// given tail if the string is truncated. +// +// It ensures the visible width, including the width of the tail, is less than or +// equal to maxWidth. +// +// When [Options.ControlSequences] is true, ANSI escape sequences that appear +// after the truncation point are preserved in the output. This ensures that +// escape sequences such as SGR resets are not lost, preventing color bleed +// in terminal output. +func (options Options) TruncateString(s string, maxWidth int, tail string) string { + maxWidthWithoutTail := maxWidth - options.String(tail) + + var pos, total int + g := graphemes.FromString(s) + g.AnsiEscapeSequences = options.ControlSequences + + for g.Next() { + gw := graphemeWidth(g.Value(), options) + if total+gw <= maxWidthWithoutTail { + pos = g.End() + } + total += gw + if total > maxWidth { + if options.ControlSequences { + // Build result with trailing ANSI escape sequences preserved + var b strings.Builder + b.Grow(len(s) + len(tail)) // at most original + tail + b.WriteString(s[:pos]) + b.WriteString(tail) + rem := graphemes.FromString(s[pos:]) + rem.AnsiEscapeSequences = true + for rem.Next() { + v := rem.Value() + if len(v) > 0 && v[0] == 0x1B { + b.WriteString(v) + } + } + return b.String() + } + return s[:pos] + tail + } + } + // No truncation + return s +} + +// TruncateString truncates a string to the given maxWidth, and appends the +// given tail if the string is truncated. +// +// It ensures the total width, including the width of the tail, is less than or +// equal to maxWidth. +func TruncateString(s string, maxWidth int, tail string) string { + return DefaultOptions.TruncateString(s, maxWidth, tail) +} + +// TruncateBytes truncates a []byte to the given maxWidth, and appends the +// given tail if the []byte is truncated. +// +// It ensures the visible width, including the width of the tail, is less than or +// equal to maxWidth. +// +// When [Options.ControlSequences] is true, ANSI escape sequences that appear +// after the truncation point are preserved in the output. This ensures that +// escape sequences such as SGR resets are not lost, preventing color bleed +// in terminal output. +func (options Options) TruncateBytes(s []byte, maxWidth int, tail []byte) []byte { + maxWidthWithoutTail := maxWidth - options.Bytes(tail) + + var pos, total int + g := graphemes.FromBytes(s) + g.AnsiEscapeSequences = options.ControlSequences + + for g.Next() { + gw := graphemeWidth(g.Value(), options) + if total+gw <= maxWidthWithoutTail { + pos = g.End() + } + total += gw + if total > maxWidth { + if options.ControlSequences { + // Build result with trailing ANSI escape sequences preserved + result := make([]byte, 0, len(s)+len(tail)) // at most original + tail + result = append(result, s[:pos]...) + result = append(result, tail...) + rem := graphemes.FromBytes(s[pos:]) + rem.AnsiEscapeSequences = true + for rem.Next() { + v := rem.Value() + if len(v) > 0 && v[0] == 0x1B { + result = append(result, v...) + } + } + return result + } + result := make([]byte, 0, pos+len(tail)) + result = append(result, s[:pos]...) + result = append(result, tail...) + return result + } + } + // No truncation + return s +} + +// TruncateBytes truncates a []byte to the given maxWidth, and appends the +// given tail if the []byte is truncated. +// +// It ensures the total width, including the width of the tail, is less than or +// equal to maxWidth. +func TruncateBytes(s []byte, maxWidth int, tail []byte) []byte { + return DefaultOptions.TruncateBytes(s, maxWidth, tail) +} + // graphemeWidth returns the display width of a grapheme cluster. // The passed string must be a single grapheme cluster. -func graphemeWidth[T stringish.Interface](s T, options Options) int { +func graphemeWidth[T ~string | []byte](s T, options Options) int { // Optimization: no need to look up properties switch len(s) { case 0: @@ -118,6 +291,11 @@ func graphemeWidth[T stringish.Interface](s T, options Options) int { return asciiWidth(s[0]) } + // Multi-byte grapheme clusters led by a C0 control (0x00-0x1F) + if s[0] <= 0x1F { + return 0 + } + p, sz := lookup(s) prop := property(p) @@ -150,9 +328,31 @@ func asciiWidth(b byte) int { return 1 } +// printableASCIILength returns the length of consecutive printable ASCII bytes +// starting at the beginning of s. +func printableASCIILength[T string | []byte](s T) int { + i := 0 + for ; i < len(s); i++ { + b := s[i] + // Printable ASCII is 0x20-0x7E (space through tilde) + if b < 0x20 || b > 0x7E { + break + } + } + + // If the next byte is non-ASCII (>= 0x80), back off by 1. The grapheme + // parser may group the last ASCII byte with subsequent non-ASCII bytes, + // such as combining marks. + if i > 0 && i < len(s) && s[i] >= 0x80 { + i-- + } + + return i +} + // isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding // (EF B8 8F). It assumes len(s) >= 3. -func isVS16[T stringish.Interface](s T) bool { +func isVS16[T ~string | []byte](s T) bool { return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F } diff --git a/vendor/github.com/clipperhouse/stringish/.gitignore b/vendor/github.com/clipperhouse/stringish/.gitignore deleted file mode 100644 index 12fbfb739b..0000000000 --- a/vendor/github.com/clipperhouse/stringish/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.DS_Store -*.test diff --git a/vendor/github.com/clipperhouse/stringish/LICENSE b/vendor/github.com/clipperhouse/stringish/LICENSE deleted file mode 100644 index 4b8064eb37..0000000000 --- a/vendor/github.com/clipperhouse/stringish/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Matt Sherman - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/clipperhouse/stringish/README.md b/vendor/github.com/clipperhouse/stringish/README.md deleted file mode 100644 index fa1f7cc672..0000000000 --- a/vendor/github.com/clipperhouse/stringish/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# stringish - -A small Go module that provides a generic type constraint for “string-like” -data, and a utf8 package that works with both strings and byte slices -without conversions. - -```go -type Interface interface { - ~[]byte | ~string -} -``` - -[![Go Reference](https://pkg.go.dev/badge/github.com/clipperhouse/stringish/utf8.svg)](https://pkg.go.dev/github.com/clipperhouse/stringish/utf8) -[![Test Status](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml/badge.svg)](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml) - -## Install - -``` -go get github.com/clipperhouse/stringish -``` - -## Examples - -```go -import ( - "github.com/clipperhouse/stringish" - "github.com/clipperhouse/stringish/utf8" -) - -s := "Hello, 世界" -r, size := utf8.DecodeRune(s) // not DecodeRuneInString 🎉 - -b := []byte("Hello, 世界") -r, size = utf8.DecodeRune(b) // same API! - -func MyFoo[T stringish.Interface](s T) T { - // pass a string or a []byte - // iterate, slice, transform, whatever -} -``` - -## Motivation - -Sometimes we want APIs to accept `string` or `[]byte` without having to convert -between those types. That conversion usually allocates! - -By implementing with `stringish.Interface`, we can have a single API, and -single implementation for both types: one `Foo` instead of `Foo` and -`FooString`. - -We have converted the -[`unicode/utf8` package](https://github.com/clipperhouse/stringish/blob/main/utf8/utf8.go) -as an example -- note the absence of`*InString` funcs. We might look at `x/text` -next. - -## Used by - -- clipperhouse/uax29: [stringish trie](https://github.com/clipperhouse/uax29/blob/master/graphemes/trie.go#L27), [stringish iterator](https://github.com/clipperhouse/uax29/blob/master/internal/iterators/iterator.go#L9), [stringish SplitFunc](https://github.com/clipperhouse/uax29/blob/master/graphemes/splitfunc.go#L21) - -- [clipperhouse/displaywidth](https://github.com/clipperhouse/displaywidth) - -## Prior discussion - -- [Consideration of similar by the Go team](https://github.com/golang/go/issues/48643) diff --git a/vendor/github.com/clipperhouse/stringish/interface.go b/vendor/github.com/clipperhouse/stringish/interface.go deleted file mode 100644 index adfeab61eb..0000000000 --- a/vendor/github.com/clipperhouse/stringish/interface.go +++ /dev/null @@ -1,5 +0,0 @@ -package stringish - -type Interface interface { - ~[]byte | ~string -} diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md b/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md index dc14d11e2f..ba355bb376 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md @@ -1,4 +1,4 @@ -An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0. +An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode 17. [![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes) ![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg) @@ -26,7 +26,7 @@ _A grapheme is a “single visible character”, which might be a simple as a si ## Conformance -We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). +We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-36.html#Tests29). ![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg) ![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg) @@ -76,15 +76,17 @@ for tokens.Next() { // Next() returns true until end of data ### Benchmarks -On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations. - ``` goos: darwin goarch: arm64 pkg: github.com/clipperhouse/uax29/graphemes/comparative cpu: Apple M2 -BenchmarkGraphemes/clipperhouse/uax29-8 173805 ns/op 201.16 MB/s 0 B/op 0 allocs/op -BenchmarkGraphemes/rivo/uniseg-8 2045128 ns/op 17.10 MB/s 0 B/op 0 allocs/op + +BenchmarkGraphemesMixed/clipperhouse/uax29-8 142635 ns/op 245.12 MB/s 0 B/op 0 allocs/op +BenchmarkGraphemesMixed/rivo/uniseg-8 2018284 ns/op 17.32 MB/s 0 B/op 0 allocs/op + +BenchmarkGraphemesASCII/clipperhouse/uax29-8 8846 ns/op 508.73 MB/s 0 B/op 0 allocs/op +BenchmarkGraphemesASCII/rivo/uniseg-8 366760 ns/op 12.27 MB/s 0 B/op 0 allocs/op ``` ### Invalid inputs diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/ansi.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/ansi.go new file mode 100644 index 0000000000..512c821bb6 --- /dev/null +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/ansi.go @@ -0,0 +1,119 @@ +package graphemes + +// ansiEscapeLength returns the byte length of a valid ANSI escape sequence at the +// start of data, or 0 if none. Input is UTF-8; only 7-bit ESC sequences are +// recognized (C1 0x80–0x9F can be UTF-8 continuation bytes). +// +// Recognized forms (ECMA-48 / ISO 6429): +// - CSI: ESC [ then parameter bytes (0x30–0x3F), intermediate (0x20–0x2F), final (0x40–0x7E) +// - OSC: ESC ] then payload until ST (ESC \) or BEL (0x07) +// - DCS, SOS, PM, APC: ESC P / X / ^ / _ then payload until ST (ESC \) +// - Two-byte: ESC + Fe (0x40–0x5F excluding above), or Fp (0x30–0x3F), or nF (0x20–0x2F then final) +func ansiEscapeLength[T ~string | ~[]byte](data T) int { + n := len(data) + if n < 2 { + return 0 + } + if data[0] != esc { + return 0 + } + + b1 := data[1] + switch b1 { + case '[': // CSI + body := csiLength(data[2:]) + if body == 0 { + return 0 + } + return 2 + body + case ']': // OSC – allows BEL or ST as terminator + body := oscLength(data[2:]) + if body == 0 { + return 0 + } + return 2 + body + case 'P', 'X', '^', '_': // DCS, SOS, PM, APC – require ST (ESC \) only + body := stSequenceLength(data[2:]) + if body == 0 { + return 0 + } + return 2 + body + } + if b1 >= 0x40 && b1 <= 0x5F { + // Fe (C1) two-byte; [ ] P X ^ _ handled above + return 2 + } + if b1 >= 0x30 && b1 <= 0x3F { + // Fp (private) two-byte + return 2 + } + if b1 >= 0x20 && b1 <= 0x2F { + // nF: intermediates then one final (0x30–0x7E) + i := 2 + for i < n && data[i] >= 0x20 && data[i] <= 0x2F { + i++ + } + if i < n && data[i] >= 0x30 && data[i] <= 0x7E { + return i + 1 + } + return 0 + } + return 0 +} + +// csiLength returns the length of the CSI body (param/intermediate/final bytes). +// data is the slice after "ESC [". +// Per ECMA-48, the CSI body has the form: +// +// parameters (0x30–0x3F)*, intermediates (0x20–0x2F)*, final (0x40–0x7E) +// +// Once an intermediate byte is seen, subsequent parameter bytes are invalid. +func csiLength[T ~string | ~[]byte](data T) int { + seenIntermediate := false + for i := 0; i < len(data); i++ { + b := data[i] + if b >= 0x30 && b <= 0x3F { + if seenIntermediate { + return 0 + } + continue + } + if b >= 0x20 && b <= 0x2F { + seenIntermediate = true + continue + } + if b >= 0x40 && b <= 0x7E { + return i + 1 + } + return 0 + } + return 0 +} + +// oscLength returns the length of the OSC body up to and including +// the terminator. OSC accepts either BEL (0x07) or ST (ESC \) per +// widespread terminal convention. data is the slice after "ESC ]". +func oscLength[T ~string | ~[]byte](data T) int { + for i := 0; i < len(data); i++ { + b := data[i] + if b == bel { + return i + 1 + } + if b == esc && i+1 < len(data) && data[i+1] == '\\' { + return i + 2 + } + } + return 0 +} + +// stSequenceLength returns the length of a control-string body up to and +// including the ST (ESC \) terminator. Used for DCS, SOS, PM, and APC, which +// per ECMA-48 require ST and do not accept BEL. data is the slice after "ESC x". +func stSequenceLength[T ~string | ~[]byte](data T) int { + for i := 0; i < len(data); i++ { + if data[i] == esc && i+1 < len(data) && data[i+1] == '\\' { + return i + 2 + } + } + return 0 +} diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go index 1eaaa534ce..a7384d7b65 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go @@ -1,12 +1,35 @@ package graphemes -import ( - "github.com/clipperhouse/stringish" - "github.com/clipperhouse/uax29/v2/internal/iterators" -) +import "unicode/utf8" -type Iterator[T stringish.Interface] struct { - *iterators.Iterator[T] +// FromString returns an iterator for the grapheme clusters in the input string. +// Iterate while Next() is true, and access the grapheme via Value(). +func FromString(s string) *Iterator[string] { + return &Iterator[string]{ + split: splitFuncString, + data: s, + } +} + +// FromBytes returns an iterator for the grapheme clusters in the input bytes. +// Iterate while Next() is true, and access the grapheme via Value(). +func FromBytes(b []byte) *Iterator[[]byte] { + return &Iterator[[]byte]{ + split: splitFuncBytes, + data: b, + } +} + +// Iterator is a generic iterator for grapheme clusters in strings or byte slices, +// with an ASCII hot path optimization. +type Iterator[T ~string | ~[]byte] struct { + split func(T, bool) (int, T, error) + data T + pos int + start int + // AnsiEscapeSequences treats ANSI escape sequences (ECMA-48) as single grapheme + // clusters when true. Default is false. + AnsiEscapeSequences bool } var ( @@ -14,18 +37,91 @@ var ( splitFuncBytes = splitFunc[[]byte] ) -// FromString returns an iterator for the grapheme clusters in the input string. -// Iterate while Next() is true, and access the grapheme via Value(). -func FromString(s string) Iterator[string] { - return Iterator[string]{ - iterators.New(splitFuncString, s), +const ( + esc = 0x1B + cr = 0x0D + bel = 0x07 +) + +// Next advances the iterator to the next grapheme cluster. +// Returns false when there are no more grapheme clusters. +func (iter *Iterator[T]) Next() bool { + if iter.pos >= len(iter.data) { + return false } + iter.start = iter.pos + + if iter.AnsiEscapeSequences && iter.data[iter.pos] == esc { + if a := ansiEscapeLength(iter.data[iter.pos:]); a > 0 { + iter.pos += a + return true + } + } + + // ASCII hot path: any ASCII is one grapheme when next byte is ASCII or end. + // Fall through on CR so splitfunc can handle CR+LF as a single cluster. + b := iter.data[iter.pos] + if b < utf8.RuneSelf && b != cr { + if iter.pos+1 >= len(iter.data) || iter.data[iter.pos+1] < utf8.RuneSelf { + iter.pos++ + return true + } + } + + // Fall back to actual grapheme parsing + remaining := iter.data[iter.pos:] + advance, _, err := iter.split(remaining, true) + if err != nil { + panic(err) + } + if advance <= 0 { + panic("splitFunc returned a zero or negative advance") + } + iter.pos += advance + if iter.pos > len(iter.data) { + panic("splitFunc advanced beyond end of data") + } + return true } -// FromBytes returns an iterator for the grapheme clusters in the input bytes. -// Iterate while Next() is true, and access the grapheme via Value(). -func FromBytes(b []byte) Iterator[[]byte] { - return Iterator[[]byte]{ - iterators.New(splitFuncBytes, b), - } +// Value returns the current grapheme cluster. +func (iter *Iterator[T]) Value() T { + return iter.data[iter.start:iter.pos] +} + +// Start returns the byte position of the current grapheme in the original data. +func (iter *Iterator[T]) Start() int { + return iter.start +} + +// End returns the byte position after the current grapheme in the original data. +func (iter *Iterator[T]) End() int { + return iter.pos +} + +// Reset resets the iterator to the beginning of the data. +func (iter *Iterator[T]) Reset() { + iter.start = 0 + iter.pos = 0 +} + +// SetText sets the data for the iterator to operate on, and resets all state. +func (iter *Iterator[T]) SetText(data T) { + iter.data = data + iter.start = 0 + iter.pos = 0 +} + +// First returns the first grapheme cluster without advancing the iterator. +func (iter *Iterator[T]) First() T { + if len(iter.data) == 0 { + return iter.data + } + + // Use a copy to leverage Next()'s ASCII optimization + cp := *iter + cp.pos = 0 + cp.start = 0 + cp.Next() + return cp.Value() } diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go index cbe1ec9ef1..0ac7c6fb45 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go @@ -2,8 +2,6 @@ package graphemes import ( "bufio" - - "github.com/clipperhouse/stringish" ) // is determines if lookup intersects propert(ies) @@ -13,12 +11,22 @@ func (lookup property) is(properties property) bool { const _Ignore = _Extend +// incbState tracks state for GB9c rule (Indic conjunct clusters) +// Pattern: Consonant (Extend|Linker)* Linker (Extend|Linker)* × Consonant +type incbState int + +const ( + incbNone incbState = iota // initial/reset + incbConsonant // seen Consonant, awaiting Linker + incbLinker // seen Consonant and Linker (conjunct ready) +) + // SplitFunc is a bufio.SplitFunc implementation of Unicode grapheme cluster segmentation, for use with bufio.Scanner. // // See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries. var SplitFunc bufio.SplitFunc = splitFunc[[]byte] -func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, err error) { +func splitFunc[T ~string | ~[]byte](data T, atEOF bool) (advance int, token T, err error) { var empty T if len(data) == 0 { return 0, empty, nil @@ -30,6 +38,9 @@ func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, var lastLastExIgnore property = 0 // "last one before that" var regionalIndicatorCount int + // GB9c state: tracking Indic conjunct clusters + var incb incbState + // Rules are usually of the form Cat1 × Cat2; "current" refers to the first property // to the right of the ×, from which we look back or forward @@ -76,6 +87,23 @@ func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, lastExIgnore = last } + // Update GB9c state based on what we just advanced past + if last.is(_InCBConsonant | _InCBLinker | _InCBExtend) { + switch { + case last.is(_InCBConsonant): + if incb != incbLinker { + incb = incbConsonant + } + case last.is(_InCBLinker): + if incb >= incbConsonant { + incb = incbLinker + } + // case last.is(_InCBExtend): stay in current state + } + } else { + incb = incbNone + } + current, w = lookup(data[pos:]) if w == 0 { if atEOF { @@ -141,11 +169,14 @@ func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, } // https://unicode.org/reports/tr29/#GB9c - // TODO(clipperhouse): - // It appears to be added in Unicode 15.1.0: - // https://unicode.org/versions/Unicode15.1.0/#Migration - // This package currently supports Unicode 15.0.0, so - // out of scope for now + // Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker. + if incb == incbLinker && current.is(_InCBConsonant) { + // After matching the pattern, reset state to start tracking a new pattern + // The current Consonant becomes the start of the new pattern + incb = incbConsonant + pos += w + continue + } // https://unicode.org/reports/tr29/#GB11 if current.is(_ExtendedPictographic) && last.is(_ZWJ) && lastLastExIgnore.is(_ExtendedPictographic) { diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go index 8aaabfacf0..56192b7ee4 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go @@ -1,17 +1,18 @@ package graphemes -import "github.com/clipperhouse/stringish" - // generated by github.com/clipperhouse/uax29/v2 -// from https://www.unicode.org/Public/15.0.0/ucd/auxiliary/GraphemeBreakProperty.txt +// from https://www.unicode.org/Public/17.0.0/ucd/auxiliary/GraphemeBreakProperty.txt -type property uint16 +type property uint32 const ( _CR property = 1 << iota _Control _Extend _ExtendedPictographic + _InCBConsonant + _InCBExtend + _InCBLinker _L _LF _LV @@ -27,7 +28,7 @@ const ( // lookup returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. -func lookup[T stringish.Interface](s T) (v property, sz int) { +func lookup[T ~string | ~[]byte](s T) (v property, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII @@ -87,7 +88,7 @@ func lookup[T stringish.Interface](s T) (v property, sz int) { return 0, 1 } -// graphemesTrie. Total size: 29120 bytes (28.44 KiB). Checksum: 80ad0c5ab9375f7. +// graphemesTrie. Total size: 61760 bytes (60.31 KiB). Checksum: af733ba94cd94ba6. // type graphemesTrie struct { } // func newGraphemesTrie(i int) *graphemesTrie { @@ -102,12 +103,12 @@ func lookupValue(n uint32, b byte) property { } } -// graphemesValues: 215 blocks, 13760 entries, 27520 bytes +// graphemesValues: 235 blocks, 15040 entries, 60160 bytes // The third block is the zero block. -var graphemesValues = [13760]property{ +var graphemesValues = [15040]property{ // Block 0x0, offset 0x0 0x00: 0x0002, 0x01: 0x0002, 0x02: 0x0002, 0x03: 0x0002, 0x04: 0x0002, 0x05: 0x0002, - 0x06: 0x0002, 0x07: 0x0002, 0x08: 0x0002, 0x09: 0x0002, 0x0a: 0x0020, 0x0b: 0x0002, + 0x06: 0x0002, 0x07: 0x0002, 0x08: 0x0002, 0x09: 0x0002, 0x0a: 0x0100, 0x0b: 0x0002, 0x0c: 0x0002, 0x0d: 0x0001, 0x0e: 0x0002, 0x0f: 0x0002, 0x10: 0x0002, 0x11: 0x0002, 0x12: 0x0002, 0x13: 0x0002, 0x14: 0x0002, 0x15: 0x0002, 0x16: 0x0002, 0x17: 0x0002, 0x18: 0x0002, 0x19: 0x0002, 0x1a: 0x0002, 0x1b: 0x0002, 0x1c: 0x0002, 0x1d: 0x0002, @@ -125,404 +126,503 @@ var graphemesValues = [13760]property{ 0xe9: 0x0008, 0xed: 0x0002, 0xee: 0x0008, // Block 0x4, offset 0x100 - 0x100: 0x0004, 0x101: 0x0004, 0x102: 0x0004, 0x103: 0x0004, 0x104: 0x0004, 0x105: 0x0004, - 0x106: 0x0004, 0x107: 0x0004, 0x108: 0x0004, 0x109: 0x0004, 0x10a: 0x0004, 0x10b: 0x0004, - 0x10c: 0x0004, 0x10d: 0x0004, 0x10e: 0x0004, 0x10f: 0x0004, 0x110: 0x0004, 0x111: 0x0004, - 0x112: 0x0004, 0x113: 0x0004, 0x114: 0x0004, 0x115: 0x0004, 0x116: 0x0004, 0x117: 0x0004, - 0x118: 0x0004, 0x119: 0x0004, 0x11a: 0x0004, 0x11b: 0x0004, 0x11c: 0x0004, 0x11d: 0x0004, - 0x11e: 0x0004, 0x11f: 0x0004, 0x120: 0x0004, 0x121: 0x0004, 0x122: 0x0004, 0x123: 0x0004, - 0x124: 0x0004, 0x125: 0x0004, 0x126: 0x0004, 0x127: 0x0004, 0x128: 0x0004, 0x129: 0x0004, - 0x12a: 0x0004, 0x12b: 0x0004, 0x12c: 0x0004, 0x12d: 0x0004, 0x12e: 0x0004, 0x12f: 0x0004, - 0x130: 0x0004, 0x131: 0x0004, 0x132: 0x0004, 0x133: 0x0004, 0x134: 0x0004, 0x135: 0x0004, - 0x136: 0x0004, 0x137: 0x0004, 0x138: 0x0004, 0x139: 0x0004, 0x13a: 0x0004, 0x13b: 0x0004, - 0x13c: 0x0004, 0x13d: 0x0004, 0x13e: 0x0004, 0x13f: 0x0004, + 0x100: 0x0024, 0x101: 0x0024, 0x102: 0x0024, 0x103: 0x0024, 0x104: 0x0024, 0x105: 0x0024, + 0x106: 0x0024, 0x107: 0x0024, 0x108: 0x0024, 0x109: 0x0024, 0x10a: 0x0024, 0x10b: 0x0024, + 0x10c: 0x0024, 0x10d: 0x0024, 0x10e: 0x0024, 0x10f: 0x0024, 0x110: 0x0024, 0x111: 0x0024, + 0x112: 0x0024, 0x113: 0x0024, 0x114: 0x0024, 0x115: 0x0024, 0x116: 0x0024, 0x117: 0x0024, + 0x118: 0x0024, 0x119: 0x0024, 0x11a: 0x0024, 0x11b: 0x0024, 0x11c: 0x0024, 0x11d: 0x0024, + 0x11e: 0x0024, 0x11f: 0x0024, 0x120: 0x0024, 0x121: 0x0024, 0x122: 0x0024, 0x123: 0x0024, + 0x124: 0x0024, 0x125: 0x0024, 0x126: 0x0024, 0x127: 0x0024, 0x128: 0x0024, 0x129: 0x0024, + 0x12a: 0x0024, 0x12b: 0x0024, 0x12c: 0x0024, 0x12d: 0x0024, 0x12e: 0x0024, 0x12f: 0x0024, + 0x130: 0x0024, 0x131: 0x0024, 0x132: 0x0024, 0x133: 0x0024, 0x134: 0x0024, 0x135: 0x0024, + 0x136: 0x0024, 0x137: 0x0024, 0x138: 0x0024, 0x139: 0x0024, 0x13a: 0x0024, 0x13b: 0x0024, + 0x13c: 0x0024, 0x13d: 0x0024, 0x13e: 0x0024, 0x13f: 0x0024, // Block 0x5, offset 0x140 - 0x140: 0x0004, 0x141: 0x0004, 0x142: 0x0004, 0x143: 0x0004, 0x144: 0x0004, 0x145: 0x0004, - 0x146: 0x0004, 0x147: 0x0004, 0x148: 0x0004, 0x149: 0x0004, 0x14a: 0x0004, 0x14b: 0x0004, - 0x14c: 0x0004, 0x14d: 0x0004, 0x14e: 0x0004, 0x14f: 0x0004, 0x150: 0x0004, 0x151: 0x0004, - 0x152: 0x0004, 0x153: 0x0004, 0x154: 0x0004, 0x155: 0x0004, 0x156: 0x0004, 0x157: 0x0004, - 0x158: 0x0004, 0x159: 0x0004, 0x15a: 0x0004, 0x15b: 0x0004, 0x15c: 0x0004, 0x15d: 0x0004, - 0x15e: 0x0004, 0x15f: 0x0004, 0x160: 0x0004, 0x161: 0x0004, 0x162: 0x0004, 0x163: 0x0004, - 0x164: 0x0004, 0x165: 0x0004, 0x166: 0x0004, 0x167: 0x0004, 0x168: 0x0004, 0x169: 0x0004, - 0x16a: 0x0004, 0x16b: 0x0004, 0x16c: 0x0004, 0x16d: 0x0004, 0x16e: 0x0004, 0x16f: 0x0004, + 0x140: 0x0024, 0x141: 0x0024, 0x142: 0x0024, 0x143: 0x0024, 0x144: 0x0024, 0x145: 0x0024, + 0x146: 0x0024, 0x147: 0x0024, 0x148: 0x0024, 0x149: 0x0024, 0x14a: 0x0024, 0x14b: 0x0024, + 0x14c: 0x0024, 0x14d: 0x0024, 0x14e: 0x0024, 0x14f: 0x0024, 0x150: 0x0024, 0x151: 0x0024, + 0x152: 0x0024, 0x153: 0x0024, 0x154: 0x0024, 0x155: 0x0024, 0x156: 0x0024, 0x157: 0x0024, + 0x158: 0x0024, 0x159: 0x0024, 0x15a: 0x0024, 0x15b: 0x0024, 0x15c: 0x0024, 0x15d: 0x0024, + 0x15e: 0x0024, 0x15f: 0x0024, 0x160: 0x0024, 0x161: 0x0024, 0x162: 0x0024, 0x163: 0x0024, + 0x164: 0x0024, 0x165: 0x0024, 0x166: 0x0024, 0x167: 0x0024, 0x168: 0x0024, 0x169: 0x0024, + 0x16a: 0x0024, 0x16b: 0x0024, 0x16c: 0x0024, 0x16d: 0x0024, 0x16e: 0x0024, 0x16f: 0x0024, // Block 0x6, offset 0x180 - 0x183: 0x0004, 0x184: 0x0004, 0x185: 0x0004, - 0x186: 0x0004, 0x187: 0x0004, 0x188: 0x0004, 0x189: 0x0004, + 0x183: 0x0024, 0x184: 0x0024, 0x185: 0x0024, + 0x186: 0x0024, 0x187: 0x0024, 0x188: 0x0024, 0x189: 0x0024, // Block 0x7, offset 0x1c0 - 0x1d1: 0x0004, - 0x1d2: 0x0004, 0x1d3: 0x0004, 0x1d4: 0x0004, 0x1d5: 0x0004, 0x1d6: 0x0004, 0x1d7: 0x0004, - 0x1d8: 0x0004, 0x1d9: 0x0004, 0x1da: 0x0004, 0x1db: 0x0004, 0x1dc: 0x0004, 0x1dd: 0x0004, - 0x1de: 0x0004, 0x1df: 0x0004, 0x1e0: 0x0004, 0x1e1: 0x0004, 0x1e2: 0x0004, 0x1e3: 0x0004, - 0x1e4: 0x0004, 0x1e5: 0x0004, 0x1e6: 0x0004, 0x1e7: 0x0004, 0x1e8: 0x0004, 0x1e9: 0x0004, - 0x1ea: 0x0004, 0x1eb: 0x0004, 0x1ec: 0x0004, 0x1ed: 0x0004, 0x1ee: 0x0004, 0x1ef: 0x0004, - 0x1f0: 0x0004, 0x1f1: 0x0004, 0x1f2: 0x0004, 0x1f3: 0x0004, 0x1f4: 0x0004, 0x1f5: 0x0004, - 0x1f6: 0x0004, 0x1f7: 0x0004, 0x1f8: 0x0004, 0x1f9: 0x0004, 0x1fa: 0x0004, 0x1fb: 0x0004, - 0x1fc: 0x0004, 0x1fd: 0x0004, 0x1ff: 0x0004, + 0x1d1: 0x0024, + 0x1d2: 0x0024, 0x1d3: 0x0024, 0x1d4: 0x0024, 0x1d5: 0x0024, 0x1d6: 0x0024, 0x1d7: 0x0024, + 0x1d8: 0x0024, 0x1d9: 0x0024, 0x1da: 0x0024, 0x1db: 0x0024, 0x1dc: 0x0024, 0x1dd: 0x0024, + 0x1de: 0x0024, 0x1df: 0x0024, 0x1e0: 0x0024, 0x1e1: 0x0024, 0x1e2: 0x0024, 0x1e3: 0x0024, + 0x1e4: 0x0024, 0x1e5: 0x0024, 0x1e6: 0x0024, 0x1e7: 0x0024, 0x1e8: 0x0024, 0x1e9: 0x0024, + 0x1ea: 0x0024, 0x1eb: 0x0024, 0x1ec: 0x0024, 0x1ed: 0x0024, 0x1ee: 0x0024, 0x1ef: 0x0024, + 0x1f0: 0x0024, 0x1f1: 0x0024, 0x1f2: 0x0024, 0x1f3: 0x0024, 0x1f4: 0x0024, 0x1f5: 0x0024, + 0x1f6: 0x0024, 0x1f7: 0x0024, 0x1f8: 0x0024, 0x1f9: 0x0024, 0x1fa: 0x0024, 0x1fb: 0x0024, + 0x1fc: 0x0024, 0x1fd: 0x0024, 0x1ff: 0x0024, // Block 0x8, offset 0x200 - 0x201: 0x0004, 0x202: 0x0004, 0x204: 0x0004, 0x205: 0x0004, - 0x207: 0x0004, + 0x201: 0x0024, 0x202: 0x0024, 0x204: 0x0024, 0x205: 0x0024, + 0x207: 0x0024, // Block 0x9, offset 0x240 - 0x240: 0x0100, 0x241: 0x0100, 0x242: 0x0100, 0x243: 0x0100, 0x244: 0x0100, 0x245: 0x0100, - 0x250: 0x0004, 0x251: 0x0004, - 0x252: 0x0004, 0x253: 0x0004, 0x254: 0x0004, 0x255: 0x0004, 0x256: 0x0004, 0x257: 0x0004, - 0x258: 0x0004, 0x259: 0x0004, 0x25a: 0x0004, 0x25c: 0x0002, + 0x240: 0x0800, 0x241: 0x0800, 0x242: 0x0800, 0x243: 0x0800, 0x244: 0x0800, 0x245: 0x0800, + 0x250: 0x0024, 0x251: 0x0024, + 0x252: 0x0024, 0x253: 0x0024, 0x254: 0x0024, 0x255: 0x0024, 0x256: 0x0024, 0x257: 0x0024, + 0x258: 0x0024, 0x259: 0x0024, 0x25a: 0x0024, 0x25c: 0x0002, // Block 0xa, offset 0x280 - 0x28b: 0x0004, - 0x28c: 0x0004, 0x28d: 0x0004, 0x28e: 0x0004, 0x28f: 0x0004, 0x290: 0x0004, 0x291: 0x0004, - 0x292: 0x0004, 0x293: 0x0004, 0x294: 0x0004, 0x295: 0x0004, 0x296: 0x0004, 0x297: 0x0004, - 0x298: 0x0004, 0x299: 0x0004, 0x29a: 0x0004, 0x29b: 0x0004, 0x29c: 0x0004, 0x29d: 0x0004, - 0x29e: 0x0004, 0x29f: 0x0004, - 0x2b0: 0x0004, + 0x28b: 0x0024, + 0x28c: 0x0024, 0x28d: 0x0024, 0x28e: 0x0024, 0x28f: 0x0024, 0x290: 0x0024, 0x291: 0x0024, + 0x292: 0x0024, 0x293: 0x0024, 0x294: 0x0024, 0x295: 0x0024, 0x296: 0x0024, 0x297: 0x0024, + 0x298: 0x0024, 0x299: 0x0024, 0x29a: 0x0024, 0x29b: 0x0024, 0x29c: 0x0024, 0x29d: 0x0024, + 0x29e: 0x0024, 0x29f: 0x0024, + 0x2b0: 0x0024, // Block 0xb, offset 0x2c0 - 0x2d6: 0x0004, 0x2d7: 0x0004, - 0x2d8: 0x0004, 0x2d9: 0x0004, 0x2da: 0x0004, 0x2db: 0x0004, 0x2dc: 0x0004, 0x2dd: 0x0100, - 0x2df: 0x0004, 0x2e0: 0x0004, 0x2e1: 0x0004, 0x2e2: 0x0004, 0x2e3: 0x0004, - 0x2e4: 0x0004, 0x2e7: 0x0004, 0x2e8: 0x0004, - 0x2ea: 0x0004, 0x2eb: 0x0004, 0x2ec: 0x0004, 0x2ed: 0x0004, + 0x2d6: 0x0024, 0x2d7: 0x0024, + 0x2d8: 0x0024, 0x2d9: 0x0024, 0x2da: 0x0024, 0x2db: 0x0024, 0x2dc: 0x0024, 0x2dd: 0x0800, + 0x2df: 0x0024, 0x2e0: 0x0024, 0x2e1: 0x0024, 0x2e2: 0x0024, 0x2e3: 0x0024, + 0x2e4: 0x0024, 0x2e7: 0x0024, 0x2e8: 0x0024, + 0x2ea: 0x0024, 0x2eb: 0x0024, 0x2ec: 0x0024, 0x2ed: 0x0024, // Block 0xc, offset 0x300 - 0x30f: 0x0100, 0x311: 0x0004, - 0x330: 0x0004, 0x331: 0x0004, 0x332: 0x0004, 0x333: 0x0004, 0x334: 0x0004, 0x335: 0x0004, - 0x336: 0x0004, 0x337: 0x0004, 0x338: 0x0004, 0x339: 0x0004, 0x33a: 0x0004, 0x33b: 0x0004, - 0x33c: 0x0004, 0x33d: 0x0004, 0x33e: 0x0004, 0x33f: 0x0004, + 0x30f: 0x0800, 0x311: 0x0024, + 0x330: 0x0024, 0x331: 0x0024, 0x332: 0x0024, 0x333: 0x0024, 0x334: 0x0024, 0x335: 0x0024, + 0x336: 0x0024, 0x337: 0x0024, 0x338: 0x0024, 0x339: 0x0024, 0x33a: 0x0024, 0x33b: 0x0024, + 0x33c: 0x0024, 0x33d: 0x0024, 0x33e: 0x0024, 0x33f: 0x0024, // Block 0xd, offset 0x340 - 0x340: 0x0004, 0x341: 0x0004, 0x342: 0x0004, 0x343: 0x0004, 0x344: 0x0004, 0x345: 0x0004, - 0x346: 0x0004, 0x347: 0x0004, 0x348: 0x0004, 0x349: 0x0004, 0x34a: 0x0004, + 0x340: 0x0024, 0x341: 0x0024, 0x342: 0x0024, 0x343: 0x0024, 0x344: 0x0024, 0x345: 0x0024, + 0x346: 0x0024, 0x347: 0x0024, 0x348: 0x0024, 0x349: 0x0024, 0x34a: 0x0024, // Block 0xe, offset 0x380 - 0x3a6: 0x0004, 0x3a7: 0x0004, 0x3a8: 0x0004, 0x3a9: 0x0004, - 0x3aa: 0x0004, 0x3ab: 0x0004, 0x3ac: 0x0004, 0x3ad: 0x0004, 0x3ae: 0x0004, 0x3af: 0x0004, - 0x3b0: 0x0004, + 0x3a6: 0x0024, 0x3a7: 0x0024, 0x3a8: 0x0024, 0x3a9: 0x0024, + 0x3aa: 0x0024, 0x3ab: 0x0024, 0x3ac: 0x0024, 0x3ad: 0x0024, 0x3ae: 0x0024, 0x3af: 0x0024, + 0x3b0: 0x0024, // Block 0xf, offset 0x3c0 - 0x3eb: 0x0004, 0x3ec: 0x0004, 0x3ed: 0x0004, 0x3ee: 0x0004, 0x3ef: 0x0004, - 0x3f0: 0x0004, 0x3f1: 0x0004, 0x3f2: 0x0004, 0x3f3: 0x0004, - 0x3fd: 0x0004, + 0x3eb: 0x0024, 0x3ec: 0x0024, 0x3ed: 0x0024, 0x3ee: 0x0024, 0x3ef: 0x0024, + 0x3f0: 0x0024, 0x3f1: 0x0024, 0x3f2: 0x0024, 0x3f3: 0x0024, + 0x3fd: 0x0024, // Block 0x10, offset 0x400 - 0x416: 0x0004, 0x417: 0x0004, - 0x418: 0x0004, 0x419: 0x0004, 0x41b: 0x0004, 0x41c: 0x0004, 0x41d: 0x0004, - 0x41e: 0x0004, 0x41f: 0x0004, 0x420: 0x0004, 0x421: 0x0004, 0x422: 0x0004, 0x423: 0x0004, - 0x425: 0x0004, 0x426: 0x0004, 0x427: 0x0004, 0x429: 0x0004, - 0x42a: 0x0004, 0x42b: 0x0004, 0x42c: 0x0004, 0x42d: 0x0004, + 0x416: 0x0024, 0x417: 0x0024, + 0x418: 0x0024, 0x419: 0x0024, 0x41b: 0x0024, 0x41c: 0x0024, 0x41d: 0x0024, + 0x41e: 0x0024, 0x41f: 0x0024, 0x420: 0x0024, 0x421: 0x0024, 0x422: 0x0024, 0x423: 0x0024, + 0x425: 0x0024, 0x426: 0x0024, 0x427: 0x0024, 0x429: 0x0024, + 0x42a: 0x0024, 0x42b: 0x0024, 0x42c: 0x0024, 0x42d: 0x0024, // Block 0x11, offset 0x440 - 0x459: 0x0004, 0x45a: 0x0004, 0x45b: 0x0004, + 0x459: 0x0024, 0x45a: 0x0024, 0x45b: 0x0024, // Block 0x12, offset 0x480 - 0x490: 0x0100, 0x491: 0x0100, - 0x498: 0x0004, 0x499: 0x0004, 0x49a: 0x0004, 0x49b: 0x0004, 0x49c: 0x0004, 0x49d: 0x0004, - 0x49e: 0x0004, 0x49f: 0x0004, + 0x490: 0x0800, 0x491: 0x0800, + 0x497: 0x0024, + 0x498: 0x0024, 0x499: 0x0024, 0x49a: 0x0024, 0x49b: 0x0024, 0x49c: 0x0024, 0x49d: 0x0024, + 0x49e: 0x0024, 0x49f: 0x0024, // Block 0x13, offset 0x4c0 - 0x4ca: 0x0004, 0x4cb: 0x0004, - 0x4cc: 0x0004, 0x4cd: 0x0004, 0x4ce: 0x0004, 0x4cf: 0x0004, 0x4d0: 0x0004, 0x4d1: 0x0004, - 0x4d2: 0x0004, 0x4d3: 0x0004, 0x4d4: 0x0004, 0x4d5: 0x0004, 0x4d6: 0x0004, 0x4d7: 0x0004, - 0x4d8: 0x0004, 0x4d9: 0x0004, 0x4da: 0x0004, 0x4db: 0x0004, 0x4dc: 0x0004, 0x4dd: 0x0004, - 0x4de: 0x0004, 0x4df: 0x0004, 0x4e0: 0x0004, 0x4e1: 0x0004, 0x4e2: 0x0100, 0x4e3: 0x0004, - 0x4e4: 0x0004, 0x4e5: 0x0004, 0x4e6: 0x0004, 0x4e7: 0x0004, 0x4e8: 0x0004, 0x4e9: 0x0004, - 0x4ea: 0x0004, 0x4eb: 0x0004, 0x4ec: 0x0004, 0x4ed: 0x0004, 0x4ee: 0x0004, 0x4ef: 0x0004, - 0x4f0: 0x0004, 0x4f1: 0x0004, 0x4f2: 0x0004, 0x4f3: 0x0004, 0x4f4: 0x0004, 0x4f5: 0x0004, - 0x4f6: 0x0004, 0x4f7: 0x0004, 0x4f8: 0x0004, 0x4f9: 0x0004, 0x4fa: 0x0004, 0x4fb: 0x0004, - 0x4fc: 0x0004, 0x4fd: 0x0004, 0x4fe: 0x0004, 0x4ff: 0x0004, + 0x4ca: 0x0024, 0x4cb: 0x0024, + 0x4cc: 0x0024, 0x4cd: 0x0024, 0x4ce: 0x0024, 0x4cf: 0x0024, 0x4d0: 0x0024, 0x4d1: 0x0024, + 0x4d2: 0x0024, 0x4d3: 0x0024, 0x4d4: 0x0024, 0x4d5: 0x0024, 0x4d6: 0x0024, 0x4d7: 0x0024, + 0x4d8: 0x0024, 0x4d9: 0x0024, 0x4da: 0x0024, 0x4db: 0x0024, 0x4dc: 0x0024, 0x4dd: 0x0024, + 0x4de: 0x0024, 0x4df: 0x0024, 0x4e0: 0x0024, 0x4e1: 0x0024, 0x4e2: 0x0800, 0x4e3: 0x0024, + 0x4e4: 0x0024, 0x4e5: 0x0024, 0x4e6: 0x0024, 0x4e7: 0x0024, 0x4e8: 0x0024, 0x4e9: 0x0024, + 0x4ea: 0x0024, 0x4eb: 0x0024, 0x4ec: 0x0024, 0x4ed: 0x0024, 0x4ee: 0x0024, 0x4ef: 0x0024, + 0x4f0: 0x0024, 0x4f1: 0x0024, 0x4f2: 0x0024, 0x4f3: 0x0024, 0x4f4: 0x0024, 0x4f5: 0x0024, + 0x4f6: 0x0024, 0x4f7: 0x0024, 0x4f8: 0x0024, 0x4f9: 0x0024, 0x4fa: 0x0024, 0x4fb: 0x0024, + 0x4fc: 0x0024, 0x4fd: 0x0024, 0x4fe: 0x0024, 0x4ff: 0x0024, // Block 0x14, offset 0x500 - 0x500: 0x0004, 0x501: 0x0004, 0x502: 0x0004, 0x503: 0x0400, - 0x53a: 0x0004, 0x53b: 0x0400, - 0x53c: 0x0004, 0x53e: 0x0400, 0x53f: 0x0400, + 0x500: 0x0024, 0x501: 0x0024, 0x502: 0x0024, 0x503: 0x2000, + 0x515: 0x0010, 0x516: 0x0010, 0x517: 0x0010, + 0x518: 0x0010, 0x519: 0x0010, 0x51a: 0x0010, 0x51b: 0x0010, 0x51c: 0x0010, 0x51d: 0x0010, + 0x51e: 0x0010, 0x51f: 0x0010, 0x520: 0x0010, 0x521: 0x0010, 0x522: 0x0010, 0x523: 0x0010, + 0x524: 0x0010, 0x525: 0x0010, 0x526: 0x0010, 0x527: 0x0010, 0x528: 0x0010, 0x529: 0x0010, + 0x52a: 0x0010, 0x52b: 0x0010, 0x52c: 0x0010, 0x52d: 0x0010, 0x52e: 0x0010, 0x52f: 0x0010, + 0x530: 0x0010, 0x531: 0x0010, 0x532: 0x0010, 0x533: 0x0010, 0x534: 0x0010, 0x535: 0x0010, + 0x536: 0x0010, 0x537: 0x0010, 0x538: 0x0010, 0x539: 0x0010, 0x53a: 0x0024, 0x53b: 0x2000, + 0x53c: 0x0024, 0x53e: 0x2000, 0x53f: 0x2000, // Block 0x15, offset 0x540 - 0x540: 0x0400, 0x541: 0x0004, 0x542: 0x0004, 0x543: 0x0004, 0x544: 0x0004, 0x545: 0x0004, - 0x546: 0x0004, 0x547: 0x0004, 0x548: 0x0004, 0x549: 0x0400, 0x54a: 0x0400, 0x54b: 0x0400, - 0x54c: 0x0400, 0x54d: 0x0004, 0x54e: 0x0400, 0x54f: 0x0400, 0x551: 0x0004, - 0x552: 0x0004, 0x553: 0x0004, 0x554: 0x0004, 0x555: 0x0004, 0x556: 0x0004, 0x557: 0x0004, - 0x562: 0x0004, 0x563: 0x0004, + 0x540: 0x2000, 0x541: 0x0024, 0x542: 0x0024, 0x543: 0x0024, 0x544: 0x0024, 0x545: 0x0024, + 0x546: 0x0024, 0x547: 0x0024, 0x548: 0x0024, 0x549: 0x2000, 0x54a: 0x2000, 0x54b: 0x2000, + 0x54c: 0x2000, 0x54d: 0x0044, 0x54e: 0x2000, 0x54f: 0x2000, 0x551: 0x0024, + 0x552: 0x0024, 0x553: 0x0024, 0x554: 0x0024, 0x555: 0x0024, 0x556: 0x0024, 0x557: 0x0024, + 0x558: 0x0010, 0x559: 0x0010, 0x55a: 0x0010, 0x55b: 0x0010, 0x55c: 0x0010, 0x55d: 0x0010, + 0x55e: 0x0010, 0x55f: 0x0010, 0x562: 0x0024, 0x563: 0x0024, + 0x578: 0x0010, 0x579: 0x0010, 0x57a: 0x0010, 0x57b: 0x0010, + 0x57c: 0x0010, 0x57d: 0x0010, 0x57e: 0x0010, 0x57f: 0x0010, // Block 0x16, offset 0x580 - 0x581: 0x0004, 0x582: 0x0400, 0x583: 0x0400, - 0x5bc: 0x0004, 0x5be: 0x0004, 0x5bf: 0x0400, + 0x581: 0x0024, 0x582: 0x2000, 0x583: 0x2000, + 0x595: 0x0010, 0x596: 0x0010, 0x597: 0x0010, + 0x598: 0x0010, 0x599: 0x0010, 0x59a: 0x0010, 0x59b: 0x0010, 0x59c: 0x0010, 0x59d: 0x0010, + 0x59e: 0x0010, 0x59f: 0x0010, 0x5a0: 0x0010, 0x5a1: 0x0010, 0x5a2: 0x0010, 0x5a3: 0x0010, + 0x5a4: 0x0010, 0x5a5: 0x0010, 0x5a6: 0x0010, 0x5a7: 0x0010, 0x5a8: 0x0010, + 0x5aa: 0x0010, 0x5ab: 0x0010, 0x5ac: 0x0010, 0x5ad: 0x0010, 0x5ae: 0x0010, 0x5af: 0x0010, + 0x5b0: 0x0010, 0x5b2: 0x0010, + 0x5b6: 0x0010, 0x5b7: 0x0010, 0x5b8: 0x0010, 0x5b9: 0x0010, + 0x5bc: 0x0024, 0x5be: 0x0024, 0x5bf: 0x2000, // Block 0x17, offset 0x5c0 - 0x5c0: 0x0400, 0x5c1: 0x0004, 0x5c2: 0x0004, 0x5c3: 0x0004, 0x5c4: 0x0004, - 0x5c7: 0x0400, 0x5c8: 0x0400, 0x5cb: 0x0400, - 0x5cc: 0x0400, 0x5cd: 0x0004, - 0x5d7: 0x0004, - 0x5e2: 0x0004, 0x5e3: 0x0004, - 0x5fe: 0x0004, + 0x5c0: 0x2000, 0x5c1: 0x0024, 0x5c2: 0x0024, 0x5c3: 0x0024, 0x5c4: 0x0024, + 0x5c7: 0x2000, 0x5c8: 0x2000, 0x5cb: 0x2000, + 0x5cc: 0x2000, 0x5cd: 0x0044, + 0x5d7: 0x0024, + 0x5dc: 0x0010, 0x5dd: 0x0010, + 0x5df: 0x0010, 0x5e2: 0x0024, 0x5e3: 0x0024, + 0x5f0: 0x0010, 0x5f1: 0x0010, + 0x5fe: 0x0024, // Block 0x18, offset 0x600 - 0x601: 0x0004, 0x602: 0x0004, 0x603: 0x0400, - 0x63c: 0x0004, 0x63e: 0x0400, 0x63f: 0x0400, + 0x601: 0x0024, 0x602: 0x0024, 0x603: 0x2000, + 0x63c: 0x0024, 0x63e: 0x2000, 0x63f: 0x2000, // Block 0x19, offset 0x640 - 0x640: 0x0400, 0x641: 0x0004, 0x642: 0x0004, - 0x647: 0x0004, 0x648: 0x0004, 0x64b: 0x0004, - 0x64c: 0x0004, 0x64d: 0x0004, 0x651: 0x0004, - 0x670: 0x0004, 0x671: 0x0004, 0x675: 0x0004, + 0x640: 0x2000, 0x641: 0x0024, 0x642: 0x0024, + 0x647: 0x0024, 0x648: 0x0024, 0x64b: 0x0024, + 0x64c: 0x0024, 0x64d: 0x0024, 0x651: 0x0024, + 0x670: 0x0024, 0x671: 0x0024, 0x675: 0x0024, // Block 0x1a, offset 0x680 - 0x680: 0x0400, 0x681: 0x0004, 0x682: 0x0004, 0x683: 0x0004, 0x684: 0x0004, 0x685: 0x0004, - 0x687: 0x0004, 0x688: 0x0004, 0x689: 0x0400, 0x68b: 0x0400, - 0x68c: 0x0400, 0x68d: 0x0004, - 0x6a2: 0x0004, 0x6a3: 0x0004, - 0x6ba: 0x0004, 0x6bb: 0x0004, - 0x6bc: 0x0004, 0x6bd: 0x0004, 0x6be: 0x0004, 0x6bf: 0x0004, + 0x681: 0x0024, 0x682: 0x0024, 0x683: 0x2000, + 0x695: 0x0010, 0x696: 0x0010, 0x697: 0x0010, + 0x698: 0x0010, 0x699: 0x0010, 0x69a: 0x0010, 0x69b: 0x0010, 0x69c: 0x0010, 0x69d: 0x0010, + 0x69e: 0x0010, 0x69f: 0x0010, 0x6a0: 0x0010, 0x6a1: 0x0010, 0x6a2: 0x0010, 0x6a3: 0x0010, + 0x6a4: 0x0010, 0x6a5: 0x0010, 0x6a6: 0x0010, 0x6a7: 0x0010, 0x6a8: 0x0010, + 0x6aa: 0x0010, 0x6ab: 0x0010, 0x6ac: 0x0010, 0x6ad: 0x0010, 0x6ae: 0x0010, 0x6af: 0x0010, + 0x6b0: 0x0010, 0x6b2: 0x0010, 0x6b3: 0x0010, 0x6b5: 0x0010, + 0x6b6: 0x0010, 0x6b7: 0x0010, 0x6b8: 0x0010, 0x6b9: 0x0010, + 0x6bc: 0x0024, 0x6be: 0x2000, 0x6bf: 0x2000, // Block 0x1b, offset 0x6c0 - 0x6c1: 0x0004, 0x6c2: 0x0400, 0x6c3: 0x0400, - 0x6fc: 0x0004, 0x6fe: 0x0004, 0x6ff: 0x0004, + 0x6c0: 0x2000, 0x6c1: 0x0024, 0x6c2: 0x0024, 0x6c3: 0x0024, 0x6c4: 0x0024, 0x6c5: 0x0024, + 0x6c7: 0x0024, 0x6c8: 0x0024, 0x6c9: 0x2000, 0x6cb: 0x2000, + 0x6cc: 0x2000, 0x6cd: 0x0044, + 0x6e2: 0x0024, 0x6e3: 0x0024, + 0x6f9: 0x0010, 0x6fa: 0x0024, 0x6fb: 0x0024, + 0x6fc: 0x0024, 0x6fd: 0x0024, 0x6fe: 0x0024, 0x6ff: 0x0024, // Block 0x1c, offset 0x700 - 0x700: 0x0400, 0x701: 0x0004, 0x702: 0x0004, 0x703: 0x0004, 0x704: 0x0004, - 0x707: 0x0400, 0x708: 0x0400, 0x70b: 0x0400, - 0x70c: 0x0400, 0x70d: 0x0004, - 0x715: 0x0004, 0x716: 0x0004, 0x717: 0x0004, - 0x722: 0x0004, 0x723: 0x0004, + 0x701: 0x0024, 0x702: 0x2000, 0x703: 0x2000, + 0x715: 0x0010, 0x716: 0x0010, 0x717: 0x0010, + 0x718: 0x0010, 0x719: 0x0010, 0x71a: 0x0010, 0x71b: 0x0010, 0x71c: 0x0010, 0x71d: 0x0010, + 0x71e: 0x0010, 0x71f: 0x0010, 0x720: 0x0010, 0x721: 0x0010, 0x722: 0x0010, 0x723: 0x0010, + 0x724: 0x0010, 0x725: 0x0010, 0x726: 0x0010, 0x727: 0x0010, 0x728: 0x0010, + 0x72a: 0x0010, 0x72b: 0x0010, 0x72c: 0x0010, 0x72d: 0x0010, 0x72e: 0x0010, 0x72f: 0x0010, + 0x730: 0x0010, 0x732: 0x0010, 0x733: 0x0010, 0x735: 0x0010, + 0x736: 0x0010, 0x737: 0x0010, 0x738: 0x0010, 0x739: 0x0010, + 0x73c: 0x0024, 0x73e: 0x0024, 0x73f: 0x0024, // Block 0x1d, offset 0x740 - 0x742: 0x0004, - 0x77e: 0x0004, 0x77f: 0x0400, + 0x740: 0x2000, 0x741: 0x0024, 0x742: 0x0024, 0x743: 0x0024, 0x744: 0x0024, + 0x747: 0x2000, 0x748: 0x2000, 0x74b: 0x2000, + 0x74c: 0x2000, 0x74d: 0x0044, + 0x755: 0x0024, 0x756: 0x0024, 0x757: 0x0024, + 0x75c: 0x0010, 0x75d: 0x0010, + 0x75f: 0x0010, 0x762: 0x0024, 0x763: 0x0024, + 0x771: 0x0010, // Block 0x1e, offset 0x780 - 0x780: 0x0004, 0x781: 0x0400, 0x782: 0x0400, - 0x786: 0x0400, 0x787: 0x0400, 0x788: 0x0400, 0x78a: 0x0400, 0x78b: 0x0400, - 0x78c: 0x0400, 0x78d: 0x0004, - 0x797: 0x0004, + 0x782: 0x0024, + 0x7be: 0x0024, 0x7bf: 0x2000, // Block 0x1f, offset 0x7c0 - 0x7c0: 0x0004, 0x7c1: 0x0400, 0x7c2: 0x0400, 0x7c3: 0x0400, 0x7c4: 0x0004, - 0x7fc: 0x0004, 0x7fe: 0x0004, 0x7ff: 0x0004, + 0x7c0: 0x0024, 0x7c1: 0x2000, 0x7c2: 0x2000, + 0x7c6: 0x2000, 0x7c7: 0x2000, 0x7c8: 0x2000, 0x7ca: 0x2000, 0x7cb: 0x2000, + 0x7cc: 0x2000, 0x7cd: 0x0024, + 0x7d7: 0x0024, // Block 0x20, offset 0x800 - 0x800: 0x0004, 0x801: 0x0400, 0x802: 0x0400, 0x803: 0x0400, 0x804: 0x0400, - 0x806: 0x0004, 0x807: 0x0004, 0x808: 0x0004, 0x80a: 0x0004, 0x80b: 0x0004, - 0x80c: 0x0004, 0x80d: 0x0004, - 0x815: 0x0004, 0x816: 0x0004, - 0x822: 0x0004, 0x823: 0x0004, + 0x800: 0x0024, 0x801: 0x2000, 0x802: 0x2000, 0x803: 0x2000, 0x804: 0x0024, + 0x815: 0x0010, 0x816: 0x0010, 0x817: 0x0010, + 0x818: 0x0010, 0x819: 0x0010, 0x81a: 0x0010, 0x81b: 0x0010, 0x81c: 0x0010, 0x81d: 0x0010, + 0x81e: 0x0010, 0x81f: 0x0010, 0x820: 0x0010, 0x821: 0x0010, 0x822: 0x0010, 0x823: 0x0010, + 0x824: 0x0010, 0x825: 0x0010, 0x826: 0x0010, 0x827: 0x0010, 0x828: 0x0010, + 0x82a: 0x0010, 0x82b: 0x0010, 0x82c: 0x0010, 0x82d: 0x0010, 0x82e: 0x0010, 0x82f: 0x0010, + 0x830: 0x0010, 0x831: 0x0010, 0x832: 0x0010, 0x833: 0x0010, 0x834: 0x0010, 0x835: 0x0010, + 0x836: 0x0010, 0x837: 0x0010, 0x838: 0x0010, 0x839: 0x0010, + 0x83c: 0x0024, 0x83e: 0x0024, 0x83f: 0x0024, // Block 0x21, offset 0x840 - 0x841: 0x0004, 0x842: 0x0400, 0x843: 0x0400, - 0x87c: 0x0004, 0x87e: 0x0400, 0x87f: 0x0004, + 0x840: 0x0024, 0x841: 0x2000, 0x842: 0x2000, 0x843: 0x2000, 0x844: 0x2000, + 0x846: 0x0024, 0x847: 0x0024, 0x848: 0x0024, 0x84a: 0x0024, 0x84b: 0x0024, + 0x84c: 0x0024, 0x84d: 0x0044, + 0x855: 0x0024, 0x856: 0x0024, + 0x858: 0x0010, 0x859: 0x0010, 0x85a: 0x0010, + 0x862: 0x0024, 0x863: 0x0024, // Block 0x22, offset 0x880 - 0x880: 0x0400, 0x881: 0x0400, 0x882: 0x0004, 0x883: 0x0400, 0x884: 0x0400, - 0x886: 0x0004, 0x887: 0x0400, 0x888: 0x0400, 0x88a: 0x0400, 0x88b: 0x0400, - 0x88c: 0x0004, 0x88d: 0x0004, - 0x895: 0x0004, 0x896: 0x0004, - 0x8a2: 0x0004, 0x8a3: 0x0004, - 0x8b3: 0x0400, + 0x881: 0x0024, 0x882: 0x2000, 0x883: 0x2000, + 0x8bc: 0x0024, 0x8be: 0x2000, 0x8bf: 0x0024, // Block 0x23, offset 0x8c0 - 0x8c0: 0x0004, 0x8c1: 0x0004, 0x8c2: 0x0400, 0x8c3: 0x0400, - 0x8fb: 0x0004, - 0x8fc: 0x0004, 0x8fe: 0x0004, 0x8ff: 0x0400, + 0x8c0: 0x0024, 0x8c1: 0x2000, 0x8c2: 0x0024, 0x8c3: 0x2000, 0x8c4: 0x2000, + 0x8c6: 0x0024, 0x8c7: 0x0024, 0x8c8: 0x0024, 0x8ca: 0x0024, 0x8cb: 0x0024, + 0x8cc: 0x0024, 0x8cd: 0x0024, + 0x8d5: 0x0024, 0x8d6: 0x0024, + 0x8e2: 0x0024, 0x8e3: 0x0024, + 0x8f3: 0x2000, // Block 0x24, offset 0x900 - 0x900: 0x0400, 0x901: 0x0004, 0x902: 0x0004, 0x903: 0x0004, 0x904: 0x0004, - 0x906: 0x0400, 0x907: 0x0400, 0x908: 0x0400, 0x90a: 0x0400, 0x90b: 0x0400, - 0x90c: 0x0400, 0x90d: 0x0004, 0x90e: 0x0100, - 0x917: 0x0004, - 0x922: 0x0004, 0x923: 0x0004, + 0x900: 0x0024, 0x901: 0x0024, 0x902: 0x2000, 0x903: 0x2000, + 0x915: 0x0010, 0x916: 0x0010, 0x917: 0x0010, + 0x918: 0x0010, 0x919: 0x0010, 0x91a: 0x0010, 0x91b: 0x0010, 0x91c: 0x0010, 0x91d: 0x0010, + 0x91e: 0x0010, 0x91f: 0x0010, 0x920: 0x0010, 0x921: 0x0010, 0x922: 0x0010, 0x923: 0x0010, + 0x924: 0x0010, 0x925: 0x0010, 0x926: 0x0010, 0x927: 0x0010, 0x928: 0x0010, 0x929: 0x0010, + 0x92a: 0x0010, 0x92b: 0x0010, 0x92c: 0x0010, 0x92d: 0x0010, 0x92e: 0x0010, 0x92f: 0x0010, + 0x930: 0x0010, 0x931: 0x0010, 0x932: 0x0010, 0x933: 0x0010, 0x934: 0x0010, 0x935: 0x0010, + 0x936: 0x0010, 0x937: 0x0010, 0x938: 0x0010, 0x939: 0x0010, 0x93a: 0x0010, 0x93b: 0x0024, + 0x93c: 0x0024, 0x93e: 0x0024, 0x93f: 0x2000, // Block 0x25, offset 0x940 - 0x941: 0x0004, 0x942: 0x0400, 0x943: 0x0400, + 0x940: 0x2000, 0x941: 0x0024, 0x942: 0x0024, 0x943: 0x0024, 0x944: 0x0024, + 0x946: 0x2000, 0x947: 0x2000, 0x948: 0x2000, 0x94a: 0x2000, 0x94b: 0x2000, + 0x94c: 0x2000, 0x94d: 0x0044, 0x94e: 0x0800, + 0x957: 0x0024, + 0x962: 0x0024, 0x963: 0x0024, // Block 0x26, offset 0x980 - 0x98a: 0x0004, - 0x98f: 0x0004, 0x990: 0x0400, 0x991: 0x0400, - 0x992: 0x0004, 0x993: 0x0004, 0x994: 0x0004, 0x996: 0x0004, - 0x998: 0x0400, 0x999: 0x0400, 0x99a: 0x0400, 0x99b: 0x0400, 0x99c: 0x0400, 0x99d: 0x0400, - 0x99e: 0x0400, 0x99f: 0x0004, - 0x9b2: 0x0400, 0x9b3: 0x0400, + 0x981: 0x0024, 0x982: 0x2000, 0x983: 0x2000, // Block 0x27, offset 0x9c0 - 0x9f1: 0x0004, 0x9f3: 0x0400, 0x9f4: 0x0004, 0x9f5: 0x0004, - 0x9f6: 0x0004, 0x9f7: 0x0004, 0x9f8: 0x0004, 0x9f9: 0x0004, 0x9fa: 0x0004, + 0x9ca: 0x0024, + 0x9cf: 0x0024, 0x9d0: 0x2000, 0x9d1: 0x2000, + 0x9d2: 0x0024, 0x9d3: 0x0024, 0x9d4: 0x0024, 0x9d6: 0x0024, + 0x9d8: 0x2000, 0x9d9: 0x2000, 0x9da: 0x2000, 0x9db: 0x2000, 0x9dc: 0x2000, 0x9dd: 0x2000, + 0x9de: 0x2000, 0x9df: 0x0024, + 0x9f2: 0x2000, 0x9f3: 0x2000, // Block 0x28, offset 0xa00 - 0xa07: 0x0004, 0xa08: 0x0004, 0xa09: 0x0004, 0xa0a: 0x0004, 0xa0b: 0x0004, - 0xa0c: 0x0004, 0xa0d: 0x0004, 0xa0e: 0x0004, + 0xa31: 0x0024, 0xa33: 0x2000, 0xa34: 0x0024, 0xa35: 0x0024, + 0xa36: 0x0024, 0xa37: 0x0024, 0xa38: 0x0024, 0xa39: 0x0024, 0xa3a: 0x0024, // Block 0x29, offset 0xa40 - 0xa71: 0x0004, 0xa73: 0x0400, 0xa74: 0x0004, 0xa75: 0x0004, - 0xa76: 0x0004, 0xa77: 0x0004, 0xa78: 0x0004, 0xa79: 0x0004, 0xa7a: 0x0004, 0xa7b: 0x0004, - 0xa7c: 0x0004, + 0xa47: 0x0024, 0xa48: 0x0024, 0xa49: 0x0024, 0xa4a: 0x0024, 0xa4b: 0x0024, + 0xa4c: 0x0024, 0xa4d: 0x0024, 0xa4e: 0x0024, // Block 0x2a, offset 0xa80 - 0xa88: 0x0004, 0xa89: 0x0004, 0xa8a: 0x0004, 0xa8b: 0x0004, - 0xa8c: 0x0004, 0xa8d: 0x0004, 0xa8e: 0x0004, + 0xab1: 0x0024, 0xab3: 0x2000, 0xab4: 0x0024, 0xab5: 0x0024, + 0xab6: 0x0024, 0xab7: 0x0024, 0xab8: 0x0024, 0xab9: 0x0024, 0xaba: 0x0024, 0xabb: 0x0024, + 0xabc: 0x0024, // Block 0x2b, offset 0xac0 - 0xad8: 0x0004, 0xad9: 0x0004, - 0xaf5: 0x0004, - 0xaf7: 0x0004, 0xaf9: 0x0004, - 0xafe: 0x0400, 0xaff: 0x0400, + 0xac8: 0x0024, 0xac9: 0x0024, 0xaca: 0x0024, 0xacb: 0x0024, + 0xacc: 0x0024, 0xacd: 0x0024, 0xace: 0x0024, // Block 0x2c, offset 0xb00 - 0xb31: 0x0004, 0xb32: 0x0004, 0xb33: 0x0004, 0xb34: 0x0004, 0xb35: 0x0004, - 0xb36: 0x0004, 0xb37: 0x0004, 0xb38: 0x0004, 0xb39: 0x0004, 0xb3a: 0x0004, 0xb3b: 0x0004, - 0xb3c: 0x0004, 0xb3d: 0x0004, 0xb3e: 0x0004, 0xb3f: 0x0400, + 0xb18: 0x0024, 0xb19: 0x0024, + 0xb35: 0x0024, + 0xb37: 0x0024, 0xb39: 0x0024, + 0xb3e: 0x2000, 0xb3f: 0x2000, // Block 0x2d, offset 0xb40 - 0xb40: 0x0004, 0xb41: 0x0004, 0xb42: 0x0004, 0xb43: 0x0004, 0xb44: 0x0004, - 0xb46: 0x0004, 0xb47: 0x0004, - 0xb4d: 0x0004, 0xb4e: 0x0004, 0xb4f: 0x0004, 0xb50: 0x0004, 0xb51: 0x0004, - 0xb52: 0x0004, 0xb53: 0x0004, 0xb54: 0x0004, 0xb55: 0x0004, 0xb56: 0x0004, 0xb57: 0x0004, - 0xb59: 0x0004, 0xb5a: 0x0004, 0xb5b: 0x0004, 0xb5c: 0x0004, 0xb5d: 0x0004, - 0xb5e: 0x0004, 0xb5f: 0x0004, 0xb60: 0x0004, 0xb61: 0x0004, 0xb62: 0x0004, 0xb63: 0x0004, - 0xb64: 0x0004, 0xb65: 0x0004, 0xb66: 0x0004, 0xb67: 0x0004, 0xb68: 0x0004, 0xb69: 0x0004, - 0xb6a: 0x0004, 0xb6b: 0x0004, 0xb6c: 0x0004, 0xb6d: 0x0004, 0xb6e: 0x0004, 0xb6f: 0x0004, - 0xb70: 0x0004, 0xb71: 0x0004, 0xb72: 0x0004, 0xb73: 0x0004, 0xb74: 0x0004, 0xb75: 0x0004, - 0xb76: 0x0004, 0xb77: 0x0004, 0xb78: 0x0004, 0xb79: 0x0004, 0xb7a: 0x0004, 0xb7b: 0x0004, - 0xb7c: 0x0004, + 0xb71: 0x0024, 0xb72: 0x0024, 0xb73: 0x0024, 0xb74: 0x0024, 0xb75: 0x0024, + 0xb76: 0x0024, 0xb77: 0x0024, 0xb78: 0x0024, 0xb79: 0x0024, 0xb7a: 0x0024, 0xb7b: 0x0024, + 0xb7c: 0x0024, 0xb7d: 0x0024, 0xb7e: 0x0024, 0xb7f: 0x2000, // Block 0x2e, offset 0xb80 - 0xb86: 0x0004, + 0xb80: 0x0024, 0xb81: 0x0024, 0xb82: 0x0024, 0xb83: 0x0024, 0xb84: 0x0024, + 0xb86: 0x0024, 0xb87: 0x0024, + 0xb8d: 0x0024, 0xb8e: 0x0024, 0xb8f: 0x0024, 0xb90: 0x0024, 0xb91: 0x0024, + 0xb92: 0x0024, 0xb93: 0x0024, 0xb94: 0x0024, 0xb95: 0x0024, 0xb96: 0x0024, 0xb97: 0x0024, + 0xb99: 0x0024, 0xb9a: 0x0024, 0xb9b: 0x0024, 0xb9c: 0x0024, 0xb9d: 0x0024, + 0xb9e: 0x0024, 0xb9f: 0x0024, 0xba0: 0x0024, 0xba1: 0x0024, 0xba2: 0x0024, 0xba3: 0x0024, + 0xba4: 0x0024, 0xba5: 0x0024, 0xba6: 0x0024, 0xba7: 0x0024, 0xba8: 0x0024, 0xba9: 0x0024, + 0xbaa: 0x0024, 0xbab: 0x0024, 0xbac: 0x0024, 0xbad: 0x0024, 0xbae: 0x0024, 0xbaf: 0x0024, + 0xbb0: 0x0024, 0xbb1: 0x0024, 0xbb2: 0x0024, 0xbb3: 0x0024, 0xbb4: 0x0024, 0xbb5: 0x0024, + 0xbb6: 0x0024, 0xbb7: 0x0024, 0xbb8: 0x0024, 0xbb9: 0x0024, 0xbba: 0x0024, 0xbbb: 0x0024, + 0xbbc: 0x0024, // Block 0x2f, offset 0xbc0 - 0xbed: 0x0004, 0xbee: 0x0004, 0xbef: 0x0004, - 0xbf0: 0x0004, 0xbf1: 0x0400, 0xbf2: 0x0004, 0xbf3: 0x0004, 0xbf4: 0x0004, 0xbf5: 0x0004, - 0xbf6: 0x0004, 0xbf7: 0x0004, 0xbf9: 0x0004, 0xbfa: 0x0004, 0xbfb: 0x0400, - 0xbfc: 0x0400, 0xbfd: 0x0004, 0xbfe: 0x0004, + 0xbc6: 0x0024, // Block 0x30, offset 0xc00 - 0xc16: 0x0400, 0xc17: 0x0400, - 0xc18: 0x0004, 0xc19: 0x0004, - 0xc1e: 0x0004, 0xc1f: 0x0004, 0xc20: 0x0004, - 0xc31: 0x0004, 0xc32: 0x0004, 0xc33: 0x0004, 0xc34: 0x0004, + 0xc00: 0x0010, 0xc01: 0x0010, 0xc02: 0x0010, 0xc03: 0x0010, 0xc04: 0x0010, 0xc05: 0x0010, + 0xc06: 0x0010, 0xc07: 0x0010, 0xc08: 0x0010, 0xc09: 0x0010, 0xc0a: 0x0010, 0xc0b: 0x0010, + 0xc0c: 0x0010, 0xc0d: 0x0010, 0xc0e: 0x0010, 0xc0f: 0x0010, 0xc10: 0x0010, 0xc11: 0x0010, + 0xc12: 0x0010, 0xc13: 0x0010, 0xc14: 0x0010, 0xc15: 0x0010, 0xc16: 0x0010, 0xc17: 0x0010, + 0xc18: 0x0010, 0xc19: 0x0010, 0xc1a: 0x0010, 0xc1b: 0x0010, 0xc1c: 0x0010, 0xc1d: 0x0010, + 0xc1e: 0x0010, 0xc1f: 0x0010, 0xc20: 0x0010, 0xc21: 0x0010, 0xc22: 0x0010, 0xc23: 0x0010, + 0xc24: 0x0010, 0xc25: 0x0010, 0xc26: 0x0010, 0xc27: 0x0010, 0xc28: 0x0010, 0xc29: 0x0010, + 0xc2a: 0x0010, 0xc2d: 0x0024, 0xc2e: 0x0024, 0xc2f: 0x0024, + 0xc30: 0x0024, 0xc31: 0x2000, 0xc32: 0x0024, 0xc33: 0x0024, 0xc34: 0x0024, 0xc35: 0x0024, + 0xc36: 0x0024, 0xc37: 0x0024, 0xc39: 0x0044, 0xc3a: 0x0024, 0xc3b: 0x2000, + 0xc3c: 0x2000, 0xc3d: 0x0024, 0xc3e: 0x0024, 0xc3f: 0x0010, // Block 0x31, offset 0xc40 - 0xc42: 0x0004, 0xc44: 0x0400, 0xc45: 0x0004, - 0xc46: 0x0004, - 0xc4d: 0x0004, - 0xc5d: 0x0004, + 0xc50: 0x0010, 0xc51: 0x0010, + 0xc52: 0x0010, 0xc53: 0x0010, 0xc54: 0x0010, 0xc55: 0x0010, 0xc56: 0x2000, 0xc57: 0x2000, + 0xc58: 0x0024, 0xc59: 0x0024, 0xc5a: 0x0010, 0xc5b: 0x0010, 0xc5c: 0x0010, 0xc5d: 0x0010, + 0xc5e: 0x0024, 0xc5f: 0x0024, 0xc60: 0x0024, 0xc61: 0x0010, + 0xc65: 0x0010, 0xc66: 0x0010, + 0xc6e: 0x0010, 0xc6f: 0x0010, + 0xc70: 0x0010, 0xc71: 0x0024, 0xc72: 0x0024, 0xc73: 0x0024, 0xc74: 0x0024, 0xc75: 0x0010, + 0xc76: 0x0010, 0xc77: 0x0010, 0xc78: 0x0010, 0xc79: 0x0010, 0xc7a: 0x0010, 0xc7b: 0x0010, + 0xc7c: 0x0010, 0xc7d: 0x0010, 0xc7e: 0x0010, 0xc7f: 0x0010, // Block 0x32, offset 0xc80 - 0xc80: 0x0010, 0xc81: 0x0010, 0xc82: 0x0010, 0xc83: 0x0010, 0xc84: 0x0010, 0xc85: 0x0010, - 0xc86: 0x0010, 0xc87: 0x0010, 0xc88: 0x0010, 0xc89: 0x0010, 0xc8a: 0x0010, 0xc8b: 0x0010, - 0xc8c: 0x0010, 0xc8d: 0x0010, 0xc8e: 0x0010, 0xc8f: 0x0010, 0xc90: 0x0010, 0xc91: 0x0010, - 0xc92: 0x0010, 0xc93: 0x0010, 0xc94: 0x0010, 0xc95: 0x0010, 0xc96: 0x0010, 0xc97: 0x0010, - 0xc98: 0x0010, 0xc99: 0x0010, 0xc9a: 0x0010, 0xc9b: 0x0010, 0xc9c: 0x0010, 0xc9d: 0x0010, - 0xc9e: 0x0010, 0xc9f: 0x0010, 0xca0: 0x0010, 0xca1: 0x0010, 0xca2: 0x0010, 0xca3: 0x0010, - 0xca4: 0x0010, 0xca5: 0x0010, 0xca6: 0x0010, 0xca7: 0x0010, 0xca8: 0x0010, 0xca9: 0x0010, - 0xcaa: 0x0010, 0xcab: 0x0010, 0xcac: 0x0010, 0xcad: 0x0010, 0xcae: 0x0010, 0xcaf: 0x0010, - 0xcb0: 0x0010, 0xcb1: 0x0010, 0xcb2: 0x0010, 0xcb3: 0x0010, 0xcb4: 0x0010, 0xcb5: 0x0010, - 0xcb6: 0x0010, 0xcb7: 0x0010, 0xcb8: 0x0010, 0xcb9: 0x0010, 0xcba: 0x0010, 0xcbb: 0x0010, - 0xcbc: 0x0010, 0xcbd: 0x0010, 0xcbe: 0x0010, 0xcbf: 0x0010, + 0xc80: 0x0010, 0xc81: 0x0010, 0xc82: 0x0024, 0xc84: 0x2000, 0xc85: 0x0024, + 0xc86: 0x0024, + 0xc8d: 0x0024, 0xc8e: 0x0010, + 0xc9d: 0x0024, // Block 0x33, offset 0xcc0 - 0xcc0: 0x0010, 0xcc1: 0x0010, 0xcc2: 0x0010, 0xcc3: 0x0010, 0xcc4: 0x0010, 0xcc5: 0x0010, - 0xcc6: 0x0010, 0xcc7: 0x0010, 0xcc8: 0x0010, 0xcc9: 0x0010, 0xcca: 0x0010, 0xccb: 0x0010, - 0xccc: 0x0010, 0xccd: 0x0010, 0xcce: 0x0010, 0xccf: 0x0010, 0xcd0: 0x0010, 0xcd1: 0x0010, - 0xcd2: 0x0010, 0xcd3: 0x0010, 0xcd4: 0x0010, 0xcd5: 0x0010, 0xcd6: 0x0010, 0xcd7: 0x0010, - 0xcd8: 0x0010, 0xcd9: 0x0010, 0xcda: 0x0010, 0xcdb: 0x0010, 0xcdc: 0x0010, 0xcdd: 0x0010, - 0xcde: 0x0010, 0xcdf: 0x0010, 0xce0: 0x1000, 0xce1: 0x1000, 0xce2: 0x1000, 0xce3: 0x1000, - 0xce4: 0x1000, 0xce5: 0x1000, 0xce6: 0x1000, 0xce7: 0x1000, 0xce8: 0x1000, 0xce9: 0x1000, - 0xcea: 0x1000, 0xceb: 0x1000, 0xcec: 0x1000, 0xced: 0x1000, 0xcee: 0x1000, 0xcef: 0x1000, - 0xcf0: 0x1000, 0xcf1: 0x1000, 0xcf2: 0x1000, 0xcf3: 0x1000, 0xcf4: 0x1000, 0xcf5: 0x1000, - 0xcf6: 0x1000, 0xcf7: 0x1000, 0xcf8: 0x1000, 0xcf9: 0x1000, 0xcfa: 0x1000, 0xcfb: 0x1000, - 0xcfc: 0x1000, 0xcfd: 0x1000, 0xcfe: 0x1000, 0xcff: 0x1000, + 0xcc0: 0x0080, 0xcc1: 0x0080, 0xcc2: 0x0080, 0xcc3: 0x0080, 0xcc4: 0x0080, 0xcc5: 0x0080, + 0xcc6: 0x0080, 0xcc7: 0x0080, 0xcc8: 0x0080, 0xcc9: 0x0080, 0xcca: 0x0080, 0xccb: 0x0080, + 0xccc: 0x0080, 0xccd: 0x0080, 0xcce: 0x0080, 0xccf: 0x0080, 0xcd0: 0x0080, 0xcd1: 0x0080, + 0xcd2: 0x0080, 0xcd3: 0x0080, 0xcd4: 0x0080, 0xcd5: 0x0080, 0xcd6: 0x0080, 0xcd7: 0x0080, + 0xcd8: 0x0080, 0xcd9: 0x0080, 0xcda: 0x0080, 0xcdb: 0x0080, 0xcdc: 0x0080, 0xcdd: 0x0080, + 0xcde: 0x0080, 0xcdf: 0x0080, 0xce0: 0x0080, 0xce1: 0x0080, 0xce2: 0x0080, 0xce3: 0x0080, + 0xce4: 0x0080, 0xce5: 0x0080, 0xce6: 0x0080, 0xce7: 0x0080, 0xce8: 0x0080, 0xce9: 0x0080, + 0xcea: 0x0080, 0xceb: 0x0080, 0xcec: 0x0080, 0xced: 0x0080, 0xcee: 0x0080, 0xcef: 0x0080, + 0xcf0: 0x0080, 0xcf1: 0x0080, 0xcf2: 0x0080, 0xcf3: 0x0080, 0xcf4: 0x0080, 0xcf5: 0x0080, + 0xcf6: 0x0080, 0xcf7: 0x0080, 0xcf8: 0x0080, 0xcf9: 0x0080, 0xcfa: 0x0080, 0xcfb: 0x0080, + 0xcfc: 0x0080, 0xcfd: 0x0080, 0xcfe: 0x0080, 0xcff: 0x0080, // Block 0x34, offset 0xd00 - 0xd00: 0x1000, 0xd01: 0x1000, 0xd02: 0x1000, 0xd03: 0x1000, 0xd04: 0x1000, 0xd05: 0x1000, - 0xd06: 0x1000, 0xd07: 0x1000, 0xd08: 0x1000, 0xd09: 0x1000, 0xd0a: 0x1000, 0xd0b: 0x1000, - 0xd0c: 0x1000, 0xd0d: 0x1000, 0xd0e: 0x1000, 0xd0f: 0x1000, 0xd10: 0x1000, 0xd11: 0x1000, - 0xd12: 0x1000, 0xd13: 0x1000, 0xd14: 0x1000, 0xd15: 0x1000, 0xd16: 0x1000, 0xd17: 0x1000, - 0xd18: 0x1000, 0xd19: 0x1000, 0xd1a: 0x1000, 0xd1b: 0x1000, 0xd1c: 0x1000, 0xd1d: 0x1000, - 0xd1e: 0x1000, 0xd1f: 0x1000, 0xd20: 0x1000, 0xd21: 0x1000, 0xd22: 0x1000, 0xd23: 0x1000, - 0xd24: 0x1000, 0xd25: 0x1000, 0xd26: 0x1000, 0xd27: 0x1000, 0xd28: 0x0800, 0xd29: 0x0800, - 0xd2a: 0x0800, 0xd2b: 0x0800, 0xd2c: 0x0800, 0xd2d: 0x0800, 0xd2e: 0x0800, 0xd2f: 0x0800, - 0xd30: 0x0800, 0xd31: 0x0800, 0xd32: 0x0800, 0xd33: 0x0800, 0xd34: 0x0800, 0xd35: 0x0800, - 0xd36: 0x0800, 0xd37: 0x0800, 0xd38: 0x0800, 0xd39: 0x0800, 0xd3a: 0x0800, 0xd3b: 0x0800, - 0xd3c: 0x0800, 0xd3d: 0x0800, 0xd3e: 0x0800, 0xd3f: 0x0800, + 0xd00: 0x0080, 0xd01: 0x0080, 0xd02: 0x0080, 0xd03: 0x0080, 0xd04: 0x0080, 0xd05: 0x0080, + 0xd06: 0x0080, 0xd07: 0x0080, 0xd08: 0x0080, 0xd09: 0x0080, 0xd0a: 0x0080, 0xd0b: 0x0080, + 0xd0c: 0x0080, 0xd0d: 0x0080, 0xd0e: 0x0080, 0xd0f: 0x0080, 0xd10: 0x0080, 0xd11: 0x0080, + 0xd12: 0x0080, 0xd13: 0x0080, 0xd14: 0x0080, 0xd15: 0x0080, 0xd16: 0x0080, 0xd17: 0x0080, + 0xd18: 0x0080, 0xd19: 0x0080, 0xd1a: 0x0080, 0xd1b: 0x0080, 0xd1c: 0x0080, 0xd1d: 0x0080, + 0xd1e: 0x0080, 0xd1f: 0x0080, 0xd20: 0x8000, 0xd21: 0x8000, 0xd22: 0x8000, 0xd23: 0x8000, + 0xd24: 0x8000, 0xd25: 0x8000, 0xd26: 0x8000, 0xd27: 0x8000, 0xd28: 0x8000, 0xd29: 0x8000, + 0xd2a: 0x8000, 0xd2b: 0x8000, 0xd2c: 0x8000, 0xd2d: 0x8000, 0xd2e: 0x8000, 0xd2f: 0x8000, + 0xd30: 0x8000, 0xd31: 0x8000, 0xd32: 0x8000, 0xd33: 0x8000, 0xd34: 0x8000, 0xd35: 0x8000, + 0xd36: 0x8000, 0xd37: 0x8000, 0xd38: 0x8000, 0xd39: 0x8000, 0xd3a: 0x8000, 0xd3b: 0x8000, + 0xd3c: 0x8000, 0xd3d: 0x8000, 0xd3e: 0x8000, 0xd3f: 0x8000, // Block 0x35, offset 0xd40 - 0xd40: 0x0800, 0xd41: 0x0800, 0xd42: 0x0800, 0xd43: 0x0800, 0xd44: 0x0800, 0xd45: 0x0800, - 0xd46: 0x0800, 0xd47: 0x0800, 0xd48: 0x0800, 0xd49: 0x0800, 0xd4a: 0x0800, 0xd4b: 0x0800, - 0xd4c: 0x0800, 0xd4d: 0x0800, 0xd4e: 0x0800, 0xd4f: 0x0800, 0xd50: 0x0800, 0xd51: 0x0800, - 0xd52: 0x0800, 0xd53: 0x0800, 0xd54: 0x0800, 0xd55: 0x0800, 0xd56: 0x0800, 0xd57: 0x0800, - 0xd58: 0x0800, 0xd59: 0x0800, 0xd5a: 0x0800, 0xd5b: 0x0800, 0xd5c: 0x0800, 0xd5d: 0x0800, - 0xd5e: 0x0800, 0xd5f: 0x0800, 0xd60: 0x0800, 0xd61: 0x0800, 0xd62: 0x0800, 0xd63: 0x0800, - 0xd64: 0x0800, 0xd65: 0x0800, 0xd66: 0x0800, 0xd67: 0x0800, 0xd68: 0x0800, 0xd69: 0x0800, - 0xd6a: 0x0800, 0xd6b: 0x0800, 0xd6c: 0x0800, 0xd6d: 0x0800, 0xd6e: 0x0800, 0xd6f: 0x0800, - 0xd70: 0x0800, 0xd71: 0x0800, 0xd72: 0x0800, 0xd73: 0x0800, 0xd74: 0x0800, 0xd75: 0x0800, - 0xd76: 0x0800, 0xd77: 0x0800, 0xd78: 0x0800, 0xd79: 0x0800, 0xd7a: 0x0800, 0xd7b: 0x0800, - 0xd7c: 0x0800, 0xd7d: 0x0800, 0xd7e: 0x0800, 0xd7f: 0x0800, + 0xd40: 0x8000, 0xd41: 0x8000, 0xd42: 0x8000, 0xd43: 0x8000, 0xd44: 0x8000, 0xd45: 0x8000, + 0xd46: 0x8000, 0xd47: 0x8000, 0xd48: 0x8000, 0xd49: 0x8000, 0xd4a: 0x8000, 0xd4b: 0x8000, + 0xd4c: 0x8000, 0xd4d: 0x8000, 0xd4e: 0x8000, 0xd4f: 0x8000, 0xd50: 0x8000, 0xd51: 0x8000, + 0xd52: 0x8000, 0xd53: 0x8000, 0xd54: 0x8000, 0xd55: 0x8000, 0xd56: 0x8000, 0xd57: 0x8000, + 0xd58: 0x8000, 0xd59: 0x8000, 0xd5a: 0x8000, 0xd5b: 0x8000, 0xd5c: 0x8000, 0xd5d: 0x8000, + 0xd5e: 0x8000, 0xd5f: 0x8000, 0xd60: 0x8000, 0xd61: 0x8000, 0xd62: 0x8000, 0xd63: 0x8000, + 0xd64: 0x8000, 0xd65: 0x8000, 0xd66: 0x8000, 0xd67: 0x8000, 0xd68: 0x4000, 0xd69: 0x4000, + 0xd6a: 0x4000, 0xd6b: 0x4000, 0xd6c: 0x4000, 0xd6d: 0x4000, 0xd6e: 0x4000, 0xd6f: 0x4000, + 0xd70: 0x4000, 0xd71: 0x4000, 0xd72: 0x4000, 0xd73: 0x4000, 0xd74: 0x4000, 0xd75: 0x4000, + 0xd76: 0x4000, 0xd77: 0x4000, 0xd78: 0x4000, 0xd79: 0x4000, 0xd7a: 0x4000, 0xd7b: 0x4000, + 0xd7c: 0x4000, 0xd7d: 0x4000, 0xd7e: 0x4000, 0xd7f: 0x4000, // Block 0x36, offset 0xd80 - 0xd9d: 0x0004, - 0xd9e: 0x0004, 0xd9f: 0x0004, + 0xd80: 0x4000, 0xd81: 0x4000, 0xd82: 0x4000, 0xd83: 0x4000, 0xd84: 0x4000, 0xd85: 0x4000, + 0xd86: 0x4000, 0xd87: 0x4000, 0xd88: 0x4000, 0xd89: 0x4000, 0xd8a: 0x4000, 0xd8b: 0x4000, + 0xd8c: 0x4000, 0xd8d: 0x4000, 0xd8e: 0x4000, 0xd8f: 0x4000, 0xd90: 0x4000, 0xd91: 0x4000, + 0xd92: 0x4000, 0xd93: 0x4000, 0xd94: 0x4000, 0xd95: 0x4000, 0xd96: 0x4000, 0xd97: 0x4000, + 0xd98: 0x4000, 0xd99: 0x4000, 0xd9a: 0x4000, 0xd9b: 0x4000, 0xd9c: 0x4000, 0xd9d: 0x4000, + 0xd9e: 0x4000, 0xd9f: 0x4000, 0xda0: 0x4000, 0xda1: 0x4000, 0xda2: 0x4000, 0xda3: 0x4000, + 0xda4: 0x4000, 0xda5: 0x4000, 0xda6: 0x4000, 0xda7: 0x4000, 0xda8: 0x4000, 0xda9: 0x4000, + 0xdaa: 0x4000, 0xdab: 0x4000, 0xdac: 0x4000, 0xdad: 0x4000, 0xdae: 0x4000, 0xdaf: 0x4000, + 0xdb0: 0x4000, 0xdb1: 0x4000, 0xdb2: 0x4000, 0xdb3: 0x4000, 0xdb4: 0x4000, 0xdb5: 0x4000, + 0xdb6: 0x4000, 0xdb7: 0x4000, 0xdb8: 0x4000, 0xdb9: 0x4000, 0xdba: 0x4000, 0xdbb: 0x4000, + 0xdbc: 0x4000, 0xdbd: 0x4000, 0xdbe: 0x4000, 0xdbf: 0x4000, // Block 0x37, offset 0xdc0 - 0xdd2: 0x0004, 0xdd3: 0x0004, 0xdd4: 0x0004, 0xdd5: 0x0400, - 0xdf2: 0x0004, 0xdf3: 0x0004, 0xdf4: 0x0400, + 0xddd: 0x0024, + 0xdde: 0x0024, 0xddf: 0x0024, // Block 0x38, offset 0xe00 - 0xe12: 0x0004, 0xe13: 0x0004, - 0xe32: 0x0004, 0xe33: 0x0004, + 0xe12: 0x0024, 0xe13: 0x0024, 0xe14: 0x0024, 0xe15: 0x0024, + 0xe32: 0x0024, 0xe33: 0x0024, 0xe34: 0x0024, // Block 0x39, offset 0xe40 - 0xe74: 0x0004, 0xe75: 0x0004, - 0xe76: 0x0400, 0xe77: 0x0004, 0xe78: 0x0004, 0xe79: 0x0004, 0xe7a: 0x0004, 0xe7b: 0x0004, - 0xe7c: 0x0004, 0xe7d: 0x0004, 0xe7e: 0x0400, 0xe7f: 0x0400, + 0xe52: 0x0024, 0xe53: 0x0024, + 0xe72: 0x0024, 0xe73: 0x0024, // Block 0x3a, offset 0xe80 - 0xe80: 0x0400, 0xe81: 0x0400, 0xe82: 0x0400, 0xe83: 0x0400, 0xe84: 0x0400, 0xe85: 0x0400, - 0xe86: 0x0004, 0xe87: 0x0400, 0xe88: 0x0400, 0xe89: 0x0004, 0xe8a: 0x0004, 0xe8b: 0x0004, - 0xe8c: 0x0004, 0xe8d: 0x0004, 0xe8e: 0x0004, 0xe8f: 0x0004, 0xe90: 0x0004, 0xe91: 0x0004, - 0xe92: 0x0004, 0xe93: 0x0004, - 0xe9d: 0x0004, + 0xe80: 0x0010, 0xe81: 0x0010, 0xe82: 0x0010, 0xe83: 0x0010, 0xe84: 0x0010, 0xe85: 0x0010, + 0xe86: 0x0010, 0xe87: 0x0010, 0xe88: 0x0010, 0xe89: 0x0010, 0xe8a: 0x0010, 0xe8b: 0x0010, + 0xe8c: 0x0010, 0xe8d: 0x0010, 0xe8e: 0x0010, 0xe8f: 0x0010, 0xe90: 0x0010, 0xe91: 0x0010, + 0xe92: 0x0010, 0xe93: 0x0010, 0xe94: 0x0010, 0xe95: 0x0010, 0xe96: 0x0010, 0xe97: 0x0010, + 0xe98: 0x0010, 0xe99: 0x0010, 0xe9a: 0x0010, 0xe9b: 0x0010, 0xe9c: 0x0010, 0xe9d: 0x0010, + 0xe9e: 0x0010, 0xe9f: 0x0010, 0xea0: 0x0010, 0xea1: 0x0010, 0xea2: 0x0010, 0xea3: 0x0010, + 0xea4: 0x0010, 0xea5: 0x0010, 0xea6: 0x0010, 0xea7: 0x0010, 0xea8: 0x0010, 0xea9: 0x0010, + 0xeaa: 0x0010, 0xeab: 0x0010, 0xeac: 0x0010, 0xead: 0x0010, 0xeae: 0x0010, 0xeaf: 0x0010, + 0xeb0: 0x0010, 0xeb1: 0x0010, 0xeb2: 0x0010, 0xeb3: 0x0010, 0xeb4: 0x0024, 0xeb5: 0x0024, + 0xeb6: 0x2000, 0xeb7: 0x0024, 0xeb8: 0x0024, 0xeb9: 0x0024, 0xeba: 0x0024, 0xebb: 0x0024, + 0xebc: 0x0024, 0xebd: 0x0024, 0xebe: 0x2000, 0xebf: 0x2000, // Block 0x3b, offset 0xec0 - 0xecb: 0x0004, - 0xecc: 0x0004, 0xecd: 0x0004, 0xece: 0x0002, 0xecf: 0x0004, + 0xec0: 0x2000, 0xec1: 0x2000, 0xec2: 0x2000, 0xec3: 0x2000, 0xec4: 0x2000, 0xec5: 0x2000, + 0xec6: 0x0024, 0xec7: 0x2000, 0xec8: 0x2000, 0xec9: 0x0024, 0xeca: 0x0024, 0xecb: 0x0024, + 0xecc: 0x0024, 0xecd: 0x0024, 0xece: 0x0024, 0xecf: 0x0024, 0xed0: 0x0024, 0xed1: 0x0024, + 0xed2: 0x0044, 0xed3: 0x0024, + 0xedd: 0x0024, // Block 0x3c, offset 0xf00 - 0xf05: 0x0004, - 0xf06: 0x0004, - 0xf29: 0x0004, + 0xf0b: 0x0024, + 0xf0c: 0x0024, 0xf0d: 0x0024, 0xf0e: 0x0002, 0xf0f: 0x0024, // Block 0x3d, offset 0xf40 - 0xf60: 0x0004, 0xf61: 0x0004, 0xf62: 0x0004, 0xf63: 0x0400, - 0xf64: 0x0400, 0xf65: 0x0400, 0xf66: 0x0400, 0xf67: 0x0004, 0xf68: 0x0004, 0xf69: 0x0400, - 0xf6a: 0x0400, 0xf6b: 0x0400, - 0xf70: 0x0400, 0xf71: 0x0400, 0xf72: 0x0004, 0xf73: 0x0400, 0xf74: 0x0400, 0xf75: 0x0400, - 0xf76: 0x0400, 0xf77: 0x0400, 0xf78: 0x0400, 0xf79: 0x0004, 0xf7a: 0x0004, 0xf7b: 0x0004, + 0xf45: 0x0024, + 0xf46: 0x0024, + 0xf69: 0x0024, // Block 0x3e, offset 0xf80 - 0xf97: 0x0004, - 0xf98: 0x0004, 0xf99: 0x0400, 0xf9a: 0x0400, 0xf9b: 0x0004, + 0xfa0: 0x0024, 0xfa1: 0x0024, 0xfa2: 0x0024, 0xfa3: 0x2000, + 0xfa4: 0x2000, 0xfa5: 0x2000, 0xfa6: 0x2000, 0xfa7: 0x0024, 0xfa8: 0x0024, 0xfa9: 0x2000, + 0xfaa: 0x2000, 0xfab: 0x2000, + 0xfb0: 0x2000, 0xfb1: 0x2000, 0xfb2: 0x0024, 0xfb3: 0x2000, 0xfb4: 0x2000, 0xfb5: 0x2000, + 0xfb6: 0x2000, 0xfb7: 0x2000, 0xfb8: 0x2000, 0xfb9: 0x0024, 0xfba: 0x0024, 0xfbb: 0x0024, // Block 0x3f, offset 0xfc0 - 0xfd5: 0x0400, 0xfd6: 0x0004, 0xfd7: 0x0400, - 0xfd8: 0x0004, 0xfd9: 0x0004, 0xfda: 0x0004, 0xfdb: 0x0004, 0xfdc: 0x0004, 0xfdd: 0x0004, - 0xfde: 0x0004, 0xfe0: 0x0004, 0xfe2: 0x0004, - 0xfe5: 0x0004, 0xfe6: 0x0004, 0xfe7: 0x0004, 0xfe8: 0x0004, 0xfe9: 0x0004, - 0xfea: 0x0004, 0xfeb: 0x0004, 0xfec: 0x0004, 0xfed: 0x0400, 0xfee: 0x0400, 0xfef: 0x0400, - 0xff0: 0x0400, 0xff1: 0x0400, 0xff2: 0x0400, 0xff3: 0x0004, 0xff4: 0x0004, 0xff5: 0x0004, - 0xff6: 0x0004, 0xff7: 0x0004, 0xff8: 0x0004, 0xff9: 0x0004, 0xffa: 0x0004, 0xffb: 0x0004, - 0xffc: 0x0004, 0xfff: 0x0004, + 0xfd7: 0x0024, + 0xfd8: 0x0024, 0xfd9: 0x2000, 0xfda: 0x2000, 0xfdb: 0x0024, + 0xfe0: 0x0010, 0xfe1: 0x0010, 0xfe2: 0x0010, 0xfe3: 0x0010, + 0xfe4: 0x0010, 0xfe5: 0x0010, 0xfe6: 0x0010, 0xfe7: 0x0010, 0xfe8: 0x0010, 0xfe9: 0x0010, + 0xfea: 0x0010, 0xfeb: 0x0010, 0xfec: 0x0010, 0xfed: 0x0010, 0xfee: 0x0010, 0xfef: 0x0010, + 0xff0: 0x0010, 0xff1: 0x0010, 0xff2: 0x0010, 0xff3: 0x0010, 0xff4: 0x0010, 0xff5: 0x0010, + 0xff6: 0x0010, 0xff7: 0x0010, 0xff8: 0x0010, 0xff9: 0x0010, 0xffa: 0x0010, 0xffb: 0x0010, + 0xffc: 0x0010, 0xffd: 0x0010, 0xffe: 0x0010, 0xfff: 0x0010, // Block 0x40, offset 0x1000 - 0x1030: 0x0004, 0x1031: 0x0004, 0x1032: 0x0004, 0x1033: 0x0004, 0x1034: 0x0004, 0x1035: 0x0004, - 0x1036: 0x0004, 0x1037: 0x0004, 0x1038: 0x0004, 0x1039: 0x0004, 0x103a: 0x0004, 0x103b: 0x0004, - 0x103c: 0x0004, 0x103d: 0x0004, 0x103e: 0x0004, 0x103f: 0x0004, + 0x1000: 0x0010, 0x1001: 0x0010, 0x1002: 0x0010, 0x1003: 0x0010, 0x1004: 0x0010, 0x1005: 0x0010, + 0x1006: 0x0010, 0x1007: 0x0010, 0x1008: 0x0010, 0x1009: 0x0010, 0x100a: 0x0010, 0x100b: 0x0010, + 0x100c: 0x0010, 0x100d: 0x0010, 0x100e: 0x0010, 0x100f: 0x0010, 0x1010: 0x0010, 0x1011: 0x0010, + 0x1012: 0x0010, 0x1013: 0x0010, 0x1014: 0x0010, 0x1015: 0x2000, 0x1016: 0x0024, 0x1017: 0x2000, + 0x1018: 0x0024, 0x1019: 0x0024, 0x101a: 0x0024, 0x101b: 0x0024, 0x101c: 0x0024, 0x101d: 0x0024, + 0x101e: 0x0024, 0x1020: 0x0044, 0x1022: 0x0024, + 0x1025: 0x0024, 0x1026: 0x0024, 0x1027: 0x0024, 0x1028: 0x0024, 0x1029: 0x0024, + 0x102a: 0x0024, 0x102b: 0x0024, 0x102c: 0x0024, 0x102d: 0x2000, 0x102e: 0x2000, 0x102f: 0x2000, + 0x1030: 0x2000, 0x1031: 0x2000, 0x1032: 0x2000, 0x1033: 0x0024, 0x1034: 0x0024, 0x1035: 0x0024, + 0x1036: 0x0024, 0x1037: 0x0024, 0x1038: 0x0024, 0x1039: 0x0024, 0x103a: 0x0024, 0x103b: 0x0024, + 0x103c: 0x0024, 0x103f: 0x0024, // Block 0x41, offset 0x1040 - 0x1040: 0x0004, 0x1041: 0x0004, 0x1042: 0x0004, 0x1043: 0x0004, 0x1044: 0x0004, 0x1045: 0x0004, - 0x1046: 0x0004, 0x1047: 0x0004, 0x1048: 0x0004, 0x1049: 0x0004, 0x104a: 0x0004, 0x104b: 0x0004, - 0x104c: 0x0004, 0x104d: 0x0004, 0x104e: 0x0004, + 0x1070: 0x0024, 0x1071: 0x0024, 0x1072: 0x0024, 0x1073: 0x0024, 0x1074: 0x0024, 0x1075: 0x0024, + 0x1076: 0x0024, 0x1077: 0x0024, 0x1078: 0x0024, 0x1079: 0x0024, 0x107a: 0x0024, 0x107b: 0x0024, + 0x107c: 0x0024, 0x107d: 0x0024, 0x107e: 0x0024, 0x107f: 0x0024, // Block 0x42, offset 0x1080 - 0x1080: 0x0004, 0x1081: 0x0004, 0x1082: 0x0004, 0x1083: 0x0004, 0x1084: 0x0400, - 0x10b4: 0x0004, 0x10b5: 0x0004, - 0x10b6: 0x0004, 0x10b7: 0x0004, 0x10b8: 0x0004, 0x10b9: 0x0004, 0x10ba: 0x0004, 0x10bb: 0x0400, - 0x10bc: 0x0004, 0x10bd: 0x0400, 0x10be: 0x0400, 0x10bf: 0x0400, + 0x1080: 0x0024, 0x1081: 0x0024, 0x1082: 0x0024, 0x1083: 0x0024, 0x1084: 0x0024, 0x1085: 0x0024, + 0x1086: 0x0024, 0x1087: 0x0024, 0x1088: 0x0024, 0x1089: 0x0024, 0x108a: 0x0024, 0x108b: 0x0024, + 0x108c: 0x0024, 0x108d: 0x0024, 0x108e: 0x0024, 0x108f: 0x0024, 0x1090: 0x0024, 0x1091: 0x0024, + 0x1092: 0x0024, 0x1093: 0x0024, 0x1094: 0x0024, 0x1095: 0x0024, 0x1096: 0x0024, 0x1097: 0x0024, + 0x1098: 0x0024, 0x1099: 0x0024, 0x109a: 0x0024, 0x109b: 0x0024, 0x109c: 0x0024, 0x109d: 0x0024, + 0x10a0: 0x0024, 0x10a1: 0x0024, 0x10a2: 0x0024, 0x10a3: 0x0024, + 0x10a4: 0x0024, 0x10a5: 0x0024, 0x10a6: 0x0024, 0x10a7: 0x0024, 0x10a8: 0x0024, 0x10a9: 0x0024, + 0x10aa: 0x0024, 0x10ab: 0x0024, // Block 0x43, offset 0x10c0 - 0x10c0: 0x0400, 0x10c1: 0x0400, 0x10c2: 0x0004, 0x10c3: 0x0400, 0x10c4: 0x0400, - 0x10eb: 0x0004, 0x10ec: 0x0004, 0x10ed: 0x0004, 0x10ee: 0x0004, 0x10ef: 0x0004, - 0x10f0: 0x0004, 0x10f1: 0x0004, 0x10f2: 0x0004, 0x10f3: 0x0004, + 0x10c0: 0x0024, 0x10c1: 0x0024, 0x10c2: 0x0024, 0x10c3: 0x0024, 0x10c4: 0x2000, + 0x10cb: 0x0010, + 0x10cc: 0x0010, + 0x10d3: 0x0010, 0x10d4: 0x0010, 0x10d5: 0x0010, 0x10d6: 0x0010, 0x10d7: 0x0010, + 0x10d8: 0x0010, 0x10d9: 0x0010, 0x10da: 0x0010, 0x10db: 0x0010, 0x10dc: 0x0010, 0x10dd: 0x0010, + 0x10de: 0x0010, 0x10df: 0x0010, 0x10e0: 0x0010, 0x10e1: 0x0010, 0x10e2: 0x0010, 0x10e3: 0x0010, + 0x10e4: 0x0010, 0x10e5: 0x0010, 0x10e6: 0x0010, 0x10e7: 0x0010, 0x10e8: 0x0010, 0x10e9: 0x0010, + 0x10ea: 0x0010, 0x10eb: 0x0010, 0x10ec: 0x0010, 0x10ed: 0x0010, 0x10ee: 0x0010, 0x10ef: 0x0010, + 0x10f0: 0x0010, 0x10f1: 0x0010, 0x10f2: 0x0010, 0x10f3: 0x0010, 0x10f4: 0x0024, 0x10f5: 0x0024, + 0x10f6: 0x0024, 0x10f7: 0x0024, 0x10f8: 0x0024, 0x10f9: 0x0024, 0x10fa: 0x0024, 0x10fb: 0x0024, + 0x10fc: 0x0024, 0x10fd: 0x0024, 0x10fe: 0x2000, 0x10ff: 0x2000, // Block 0x44, offset 0x1100 - 0x1100: 0x0004, 0x1101: 0x0004, 0x1102: 0x0400, - 0x1121: 0x0400, 0x1122: 0x0004, 0x1123: 0x0004, - 0x1124: 0x0004, 0x1125: 0x0004, 0x1126: 0x0400, 0x1127: 0x0400, 0x1128: 0x0004, 0x1129: 0x0004, - 0x112a: 0x0400, 0x112b: 0x0004, 0x112c: 0x0004, 0x112d: 0x0004, + 0x1100: 0x2000, 0x1101: 0x2000, 0x1102: 0x0024, 0x1103: 0x0024, 0x1104: 0x0044, 0x1105: 0x0010, + 0x1106: 0x0010, 0x1107: 0x0010, 0x1108: 0x0010, 0x1109: 0x0010, 0x110a: 0x0010, 0x110b: 0x0010, + 0x110c: 0x0010, + 0x112b: 0x0024, 0x112c: 0x0024, 0x112d: 0x0024, 0x112e: 0x0024, 0x112f: 0x0024, + 0x1130: 0x0024, 0x1131: 0x0024, 0x1132: 0x0024, 0x1133: 0x0024, // Block 0x45, offset 0x1140 - 0x1166: 0x0004, 0x1167: 0x0400, 0x1168: 0x0004, 0x1169: 0x0004, - 0x116a: 0x0400, 0x116b: 0x0400, 0x116c: 0x0400, 0x116d: 0x0004, 0x116e: 0x0400, 0x116f: 0x0004, - 0x1170: 0x0004, 0x1171: 0x0004, 0x1172: 0x0400, 0x1173: 0x0400, + 0x1140: 0x0024, 0x1141: 0x0024, 0x1142: 0x2000, 0x1143: 0x0010, 0x1144: 0x0010, 0x1145: 0x0010, + 0x1146: 0x0010, 0x1147: 0x0010, 0x1148: 0x0010, 0x1149: 0x0010, 0x114a: 0x0010, 0x114b: 0x0010, + 0x114c: 0x0010, 0x114d: 0x0010, 0x114e: 0x0010, 0x114f: 0x0010, 0x1150: 0x0010, 0x1151: 0x0010, + 0x1152: 0x0010, 0x1153: 0x0010, 0x1154: 0x0010, 0x1155: 0x0010, 0x1156: 0x0010, 0x1157: 0x0010, + 0x1158: 0x0010, 0x1159: 0x0010, 0x115a: 0x0010, 0x115b: 0x0010, 0x115c: 0x0010, 0x115d: 0x0010, + 0x115e: 0x0010, 0x115f: 0x0010, 0x1160: 0x0010, 0x1161: 0x2000, 0x1162: 0x0024, 0x1163: 0x0024, + 0x1164: 0x0024, 0x1165: 0x0024, 0x1166: 0x2000, 0x1167: 0x2000, 0x1168: 0x0024, 0x1169: 0x0024, + 0x116a: 0x0024, 0x116b: 0x0044, 0x116c: 0x0024, 0x116d: 0x0024, 0x116e: 0x0010, 0x116f: 0x0010, + 0x117b: 0x0010, + 0x117c: 0x0010, 0x117d: 0x0010, // Block 0x46, offset 0x1180 - 0x11a4: 0x0400, 0x11a5: 0x0400, 0x11a6: 0x0400, 0x11a7: 0x0400, 0x11a8: 0x0400, 0x11a9: 0x0400, - 0x11aa: 0x0400, 0x11ab: 0x0400, 0x11ac: 0x0004, 0x11ad: 0x0004, 0x11ae: 0x0004, 0x11af: 0x0004, - 0x11b0: 0x0004, 0x11b1: 0x0004, 0x11b2: 0x0004, 0x11b3: 0x0004, 0x11b4: 0x0400, 0x11b5: 0x0400, - 0x11b6: 0x0004, 0x11b7: 0x0004, + 0x11a6: 0x0024, 0x11a7: 0x2000, 0x11a8: 0x0024, 0x11a9: 0x0024, + 0x11aa: 0x2000, 0x11ab: 0x2000, 0x11ac: 0x2000, 0x11ad: 0x0024, 0x11ae: 0x2000, 0x11af: 0x0024, + 0x11b0: 0x0024, 0x11b1: 0x0024, 0x11b2: 0x0024, 0x11b3: 0x0024, // Block 0x47, offset 0x11c0 - 0x11d0: 0x0004, 0x11d1: 0x0004, - 0x11d2: 0x0004, 0x11d4: 0x0004, 0x11d5: 0x0004, 0x11d6: 0x0004, 0x11d7: 0x0004, - 0x11d8: 0x0004, 0x11d9: 0x0004, 0x11da: 0x0004, 0x11db: 0x0004, 0x11dc: 0x0004, 0x11dd: 0x0004, - 0x11de: 0x0004, 0x11df: 0x0004, 0x11e0: 0x0004, 0x11e1: 0x0400, 0x11e2: 0x0004, 0x11e3: 0x0004, - 0x11e4: 0x0004, 0x11e5: 0x0004, 0x11e6: 0x0004, 0x11e7: 0x0004, 0x11e8: 0x0004, - 0x11ed: 0x0004, - 0x11f4: 0x0004, - 0x11f7: 0x0400, 0x11f8: 0x0004, 0x11f9: 0x0004, + 0x11e4: 0x2000, 0x11e5: 0x2000, 0x11e6: 0x2000, 0x11e7: 0x2000, 0x11e8: 0x2000, 0x11e9: 0x2000, + 0x11ea: 0x2000, 0x11eb: 0x2000, 0x11ec: 0x0024, 0x11ed: 0x0024, 0x11ee: 0x0024, 0x11ef: 0x0024, + 0x11f0: 0x0024, 0x11f1: 0x0024, 0x11f2: 0x0024, 0x11f3: 0x0024, 0x11f4: 0x2000, 0x11f5: 0x2000, + 0x11f6: 0x0024, 0x11f7: 0x0024, // Block 0x48, offset 0x1200 - 0x120b: 0x0002, - 0x120c: 0x0004, 0x120d: 0x2000, 0x120e: 0x0002, 0x120f: 0x0002, - 0x1228: 0x0002, 0x1229: 0x0002, - 0x122a: 0x0002, 0x122b: 0x0002, 0x122c: 0x0002, 0x122d: 0x0002, 0x122e: 0x0002, - 0x123c: 0x0008, + 0x1210: 0x0024, 0x1211: 0x0024, + 0x1212: 0x0024, 0x1214: 0x0024, 0x1215: 0x0024, 0x1216: 0x0024, 0x1217: 0x0024, + 0x1218: 0x0024, 0x1219: 0x0024, 0x121a: 0x0024, 0x121b: 0x0024, 0x121c: 0x0024, 0x121d: 0x0024, + 0x121e: 0x0024, 0x121f: 0x0024, 0x1220: 0x0024, 0x1221: 0x2000, 0x1222: 0x0024, 0x1223: 0x0024, + 0x1224: 0x0024, 0x1225: 0x0024, 0x1226: 0x0024, 0x1227: 0x0024, 0x1228: 0x0024, + 0x122d: 0x0024, + 0x1234: 0x0024, + 0x1237: 0x2000, 0x1238: 0x0024, 0x1239: 0x0024, // Block 0x49, offset 0x1240 - 0x1249: 0x0008, - 0x1260: 0x0002, 0x1261: 0x0002, 0x1262: 0x0002, 0x1263: 0x0002, - 0x1264: 0x0002, 0x1265: 0x0002, 0x1266: 0x0002, 0x1267: 0x0002, 0x1268: 0x0002, 0x1269: 0x0002, - 0x126a: 0x0002, 0x126b: 0x0002, 0x126c: 0x0002, 0x126d: 0x0002, 0x126e: 0x0002, 0x126f: 0x0002, + 0x124b: 0x0002, + 0x124c: 0x0004, 0x124d: 0x10020, 0x124e: 0x0002, 0x124f: 0x0002, + 0x1268: 0x0002, 0x1269: 0x0002, + 0x126a: 0x0002, 0x126b: 0x0002, 0x126c: 0x0002, 0x126d: 0x0002, 0x126e: 0x0002, + 0x127c: 0x0008, // Block 0x4a, offset 0x1280 - 0x1290: 0x0004, 0x1291: 0x0004, - 0x1292: 0x0004, 0x1293: 0x0004, 0x1294: 0x0004, 0x1295: 0x0004, 0x1296: 0x0004, 0x1297: 0x0004, - 0x1298: 0x0004, 0x1299: 0x0004, 0x129a: 0x0004, 0x129b: 0x0004, 0x129c: 0x0004, 0x129d: 0x0004, - 0x129e: 0x0004, 0x129f: 0x0004, 0x12a0: 0x0004, 0x12a1: 0x0004, 0x12a2: 0x0004, 0x12a3: 0x0004, - 0x12a4: 0x0004, 0x12a5: 0x0004, 0x12a6: 0x0004, 0x12a7: 0x0004, 0x12a8: 0x0004, 0x12a9: 0x0004, - 0x12aa: 0x0004, 0x12ab: 0x0004, 0x12ac: 0x0004, 0x12ad: 0x0004, 0x12ae: 0x0004, 0x12af: 0x0004, - 0x12b0: 0x0004, + 0x1289: 0x0008, + 0x12a0: 0x0002, 0x12a1: 0x0002, 0x12a2: 0x0002, 0x12a3: 0x0002, + 0x12a4: 0x0002, 0x12a5: 0x0002, 0x12a6: 0x0002, 0x12a7: 0x0002, 0x12a8: 0x0002, 0x12a9: 0x0002, + 0x12aa: 0x0002, 0x12ab: 0x0002, 0x12ac: 0x0002, 0x12ad: 0x0002, 0x12ae: 0x0002, 0x12af: 0x0002, // Block 0x4b, offset 0x12c0 - 0x12e2: 0x0008, - 0x12f9: 0x0008, + 0x12d0: 0x0024, 0x12d1: 0x0024, + 0x12d2: 0x0024, 0x12d3: 0x0024, 0x12d4: 0x0024, 0x12d5: 0x0024, 0x12d6: 0x0024, 0x12d7: 0x0024, + 0x12d8: 0x0024, 0x12d9: 0x0024, 0x12da: 0x0024, 0x12db: 0x0024, 0x12dc: 0x0024, 0x12dd: 0x0024, + 0x12de: 0x0024, 0x12df: 0x0024, 0x12e0: 0x0024, 0x12e1: 0x0024, 0x12e2: 0x0024, 0x12e3: 0x0024, + 0x12e4: 0x0024, 0x12e5: 0x0024, 0x12e6: 0x0024, 0x12e7: 0x0024, 0x12e8: 0x0024, 0x12e9: 0x0024, + 0x12ea: 0x0024, 0x12eb: 0x0024, 0x12ec: 0x0024, 0x12ed: 0x0024, 0x12ee: 0x0024, 0x12ef: 0x0024, + 0x12f0: 0x0024, // Block 0x4c, offset 0x1300 - 0x1314: 0x0008, 0x1315: 0x0008, 0x1316: 0x0008, 0x1317: 0x0008, - 0x1318: 0x0008, 0x1319: 0x0008, - 0x1329: 0x0008, - 0x132a: 0x0008, + 0x1322: 0x0008, + 0x1339: 0x0008, // Block 0x4d, offset 0x1340 - 0x135a: 0x0008, 0x135b: 0x0008, - 0x1368: 0x0008, + 0x1354: 0x0008, 0x1355: 0x0008, 0x1356: 0x0008, 0x1357: 0x0008, + 0x1358: 0x0008, 0x1359: 0x0008, + 0x1369: 0x0008, + 0x136a: 0x0008, // Block 0x4e, offset 0x1380 - 0x1388: 0x0008, + 0x139a: 0x0008, 0x139b: 0x0008, + 0x13a8: 0x0008, // Block 0x4f, offset 0x13c0 0x13cf: 0x0008, 0x13e9: 0x0008, @@ -539,746 +639,951 @@ var graphemesValues = [13760]property{ 0x14bb: 0x0008, 0x14bc: 0x0008, 0x14bd: 0x0008, 0x14be: 0x0008, // Block 0x53, offset 0x14c0 - 0x14c0: 0x0008, 0x14c1: 0x0008, 0x14c2: 0x0008, 0x14c3: 0x0008, 0x14c4: 0x0008, 0x14c5: 0x0008, - 0x14c7: 0x0008, 0x14c8: 0x0008, 0x14c9: 0x0008, 0x14ca: 0x0008, 0x14cb: 0x0008, - 0x14cc: 0x0008, 0x14cd: 0x0008, 0x14ce: 0x0008, 0x14cf: 0x0008, 0x14d0: 0x0008, 0x14d1: 0x0008, - 0x14d2: 0x0008, 0x14d4: 0x0008, 0x14d5: 0x0008, 0x14d6: 0x0008, 0x14d7: 0x0008, - 0x14d8: 0x0008, 0x14d9: 0x0008, 0x14da: 0x0008, 0x14db: 0x0008, 0x14dc: 0x0008, 0x14dd: 0x0008, - 0x14de: 0x0008, 0x14df: 0x0008, 0x14e0: 0x0008, 0x14e1: 0x0008, 0x14e2: 0x0008, 0x14e3: 0x0008, - 0x14e4: 0x0008, 0x14e5: 0x0008, 0x14e6: 0x0008, 0x14e7: 0x0008, 0x14e8: 0x0008, 0x14e9: 0x0008, - 0x14ea: 0x0008, 0x14eb: 0x0008, 0x14ec: 0x0008, 0x14ed: 0x0008, 0x14ee: 0x0008, 0x14ef: 0x0008, - 0x14f0: 0x0008, 0x14f1: 0x0008, 0x14f2: 0x0008, 0x14f3: 0x0008, 0x14f4: 0x0008, 0x14f5: 0x0008, - 0x14f6: 0x0008, 0x14f7: 0x0008, 0x14f8: 0x0008, 0x14f9: 0x0008, 0x14fa: 0x0008, 0x14fb: 0x0008, - 0x14fc: 0x0008, 0x14fd: 0x0008, 0x14fe: 0x0008, 0x14ff: 0x0008, + 0x14c0: 0x0008, 0x14c1: 0x0008, 0x14c2: 0x0008, 0x14c3: 0x0008, 0x14c4: 0x0008, + 0x14ce: 0x0008, 0x14d1: 0x0008, + 0x14d4: 0x0008, 0x14d5: 0x0008, + 0x14d8: 0x0008, 0x14dd: 0x0008, + 0x14e0: 0x0008, 0x14e2: 0x0008, 0x14e3: 0x0008, + 0x14e6: 0x0008, + 0x14ea: 0x0008, 0x14ee: 0x0008, 0x14ef: 0x0008, + 0x14f8: 0x0008, 0x14f9: 0x0008, 0x14fa: 0x0008, // Block 0x54, offset 0x1500 - 0x1500: 0x0008, 0x1501: 0x0008, 0x1502: 0x0008, 0x1503: 0x0008, 0x1504: 0x0008, 0x1505: 0x0008, - 0x1506: 0x0008, 0x1507: 0x0008, 0x1508: 0x0008, 0x1509: 0x0008, 0x150a: 0x0008, 0x150b: 0x0008, + 0x1500: 0x0008, 0x1502: 0x0008, + 0x1508: 0x0008, 0x1509: 0x0008, 0x150a: 0x0008, 0x150b: 0x0008, 0x150c: 0x0008, 0x150d: 0x0008, 0x150e: 0x0008, 0x150f: 0x0008, 0x1510: 0x0008, 0x1511: 0x0008, - 0x1512: 0x0008, 0x1513: 0x0008, 0x1514: 0x0008, 0x1515: 0x0008, 0x1516: 0x0008, 0x1517: 0x0008, - 0x1518: 0x0008, 0x1519: 0x0008, 0x151a: 0x0008, 0x151b: 0x0008, 0x151c: 0x0008, 0x151d: 0x0008, - 0x151e: 0x0008, 0x151f: 0x0008, 0x1520: 0x0008, 0x1521: 0x0008, 0x1522: 0x0008, 0x1523: 0x0008, - 0x1524: 0x0008, 0x1525: 0x0008, 0x1526: 0x0008, 0x1527: 0x0008, 0x1528: 0x0008, 0x1529: 0x0008, - 0x152a: 0x0008, 0x152b: 0x0008, 0x152c: 0x0008, 0x152d: 0x0008, 0x152e: 0x0008, 0x152f: 0x0008, - 0x1530: 0x0008, 0x1531: 0x0008, 0x1532: 0x0008, 0x1533: 0x0008, 0x1534: 0x0008, 0x1535: 0x0008, - 0x1536: 0x0008, 0x1537: 0x0008, 0x1538: 0x0008, 0x1539: 0x0008, 0x153a: 0x0008, 0x153b: 0x0008, - 0x153c: 0x0008, 0x153d: 0x0008, 0x153e: 0x0008, 0x153f: 0x0008, + 0x1512: 0x0008, 0x1513: 0x0008, + 0x151f: 0x0008, 0x1520: 0x0008, 0x1523: 0x0008, + 0x1525: 0x0008, 0x1526: 0x0008, 0x1528: 0x0008, + 0x153b: 0x0008, + 0x153e: 0x0008, 0x153f: 0x0008, // Block 0x55, offset 0x1540 - 0x1540: 0x0008, 0x1541: 0x0008, 0x1542: 0x0008, 0x1543: 0x0008, 0x1544: 0x0008, 0x1545: 0x0008, - 0x1550: 0x0008, 0x1551: 0x0008, 0x1552: 0x0008, 0x1553: 0x0008, 0x1554: 0x0008, 0x1555: 0x0008, 0x1556: 0x0008, 0x1557: 0x0008, - 0x1558: 0x0008, 0x1559: 0x0008, 0x155a: 0x0008, 0x155b: 0x0008, 0x155c: 0x0008, 0x155d: 0x0008, - 0x155e: 0x0008, 0x155f: 0x0008, 0x1560: 0x0008, 0x1561: 0x0008, 0x1562: 0x0008, 0x1563: 0x0008, - 0x1564: 0x0008, 0x1565: 0x0008, 0x1566: 0x0008, 0x1567: 0x0008, 0x1568: 0x0008, 0x1569: 0x0008, - 0x156a: 0x0008, 0x156b: 0x0008, 0x156c: 0x0008, 0x156d: 0x0008, 0x156e: 0x0008, 0x156f: 0x0008, - 0x1570: 0x0008, 0x1571: 0x0008, 0x1572: 0x0008, 0x1573: 0x0008, 0x1574: 0x0008, 0x1575: 0x0008, - 0x1576: 0x0008, 0x1577: 0x0008, 0x1578: 0x0008, 0x1579: 0x0008, 0x157a: 0x0008, 0x157b: 0x0008, - 0x157c: 0x0008, 0x157d: 0x0008, 0x157e: 0x0008, 0x157f: 0x0008, + 0x1559: 0x0008, 0x155b: 0x0008, 0x155c: 0x0008, + 0x1560: 0x0008, 0x1561: 0x0008, + 0x1567: 0x0008, + 0x156a: 0x0008, 0x156b: 0x0008, + 0x1570: 0x0008, 0x1571: 0x0008, + 0x157d: 0x0008, 0x157e: 0x0008, // Block 0x56, offset 0x1580 - 0x1580: 0x0008, 0x1581: 0x0008, 0x1582: 0x0008, 0x1583: 0x0008, 0x1584: 0x0008, 0x1585: 0x0008, - 0x1588: 0x0008, 0x1589: 0x0008, 0x158a: 0x0008, 0x158b: 0x0008, - 0x158c: 0x0008, 0x158d: 0x0008, 0x158e: 0x0008, 0x158f: 0x0008, 0x1590: 0x0008, 0x1591: 0x0008, - 0x1592: 0x0008, 0x1594: 0x0008, 0x1596: 0x0008, - 0x159d: 0x0008, - 0x15a1: 0x0008, - 0x15a8: 0x0008, - 0x15b3: 0x0008, 0x15b4: 0x0008, + 0x1584: 0x0008, 0x1585: 0x0008, + 0x1588: 0x0008, + 0x158e: 0x0008, 0x158f: 0x0008, 0x1591: 0x0008, + 0x1593: 0x0008, 0x1594: 0x0008, + 0x15a9: 0x0008, + 0x15aa: 0x0008, + 0x15b0: 0x0008, 0x15b1: 0x0008, 0x15b2: 0x0008, 0x15b3: 0x0008, 0x15b4: 0x0008, 0x15b5: 0x0008, + 0x15b7: 0x0008, 0x15b8: 0x0008, 0x15b9: 0x0008, 0x15ba: 0x0008, + 0x15bd: 0x0008, // Block 0x57, offset 0x15c0 - 0x15c4: 0x0008, - 0x15c7: 0x0008, - 0x15cc: 0x0008, 0x15ce: 0x0008, - 0x15d3: 0x0008, 0x15d4: 0x0008, 0x15d5: 0x0008, 0x15d7: 0x0008, - 0x15e3: 0x0008, - 0x15e4: 0x0008, 0x15e5: 0x0008, 0x15e6: 0x0008, 0x15e7: 0x0008, + 0x15c2: 0x0008, 0x15c5: 0x0008, + 0x15c8: 0x0008, 0x15c9: 0x0008, 0x15ca: 0x0008, 0x15cb: 0x0008, + 0x15cc: 0x0008, 0x15cd: 0x0008, 0x15cf: 0x0008, + 0x15d2: 0x0008, 0x15d4: 0x0008, 0x15d6: 0x0008, + 0x15dd: 0x0008, + 0x15e1: 0x0008, + 0x15e8: 0x0008, + 0x15f3: 0x0008, 0x15f4: 0x0008, // Block 0x58, offset 0x1600 - 0x1615: 0x0008, 0x1616: 0x0008, 0x1617: 0x0008, - 0x1621: 0x0008, - 0x1630: 0x0008, - 0x163f: 0x0008, + 0x1604: 0x0008, + 0x1607: 0x0008, + 0x160c: 0x0008, 0x160e: 0x0008, + 0x1613: 0x0008, 0x1614: 0x0008, 0x1615: 0x0008, 0x1617: 0x0008, + 0x1623: 0x0008, + 0x1624: 0x0008, // Block 0x59, offset 0x1640 - 0x1674: 0x0008, 0x1675: 0x0008, + 0x1655: 0x0008, 0x1656: 0x0008, 0x1657: 0x0008, + 0x1661: 0x0008, + 0x1670: 0x0008, + 0x167f: 0x0008, // Block 0x5a, offset 0x1680 - 0x1685: 0x0008, - 0x1686: 0x0008, 0x1687: 0x0008, - 0x169b: 0x0008, 0x169c: 0x0008, + 0x16b4: 0x0008, 0x16b5: 0x0008, // Block 0x5b, offset 0x16c0 - 0x16d0: 0x0008, - 0x16d5: 0x0008, + 0x16c5: 0x0008, + 0x16c6: 0x0008, 0x16c7: 0x0008, + 0x16db: 0x0008, 0x16dc: 0x0008, // Block 0x5c, offset 0x1700 - 0x172f: 0x0004, - 0x1730: 0x0004, 0x1731: 0x0004, + 0x1710: 0x0008, + 0x1715: 0x0008, // Block 0x5d, offset 0x1740 - 0x177f: 0x0004, + 0x176f: 0x0024, + 0x1770: 0x0024, 0x1771: 0x0024, // Block 0x5e, offset 0x1780 - 0x17a0: 0x0004, 0x17a1: 0x0004, 0x17a2: 0x0004, 0x17a3: 0x0004, - 0x17a4: 0x0004, 0x17a5: 0x0004, 0x17a6: 0x0004, 0x17a7: 0x0004, 0x17a8: 0x0004, 0x17a9: 0x0004, - 0x17aa: 0x0004, 0x17ab: 0x0004, 0x17ac: 0x0004, 0x17ad: 0x0004, 0x17ae: 0x0004, 0x17af: 0x0004, - 0x17b0: 0x0004, 0x17b1: 0x0004, 0x17b2: 0x0004, 0x17b3: 0x0004, 0x17b4: 0x0004, 0x17b5: 0x0004, - 0x17b6: 0x0004, 0x17b7: 0x0004, 0x17b8: 0x0004, 0x17b9: 0x0004, 0x17ba: 0x0004, 0x17bb: 0x0004, - 0x17bc: 0x0004, 0x17bd: 0x0004, 0x17be: 0x0004, 0x17bf: 0x0004, + 0x17bf: 0x0024, // Block 0x5f, offset 0x17c0 - 0x17ea: 0x0004, 0x17eb: 0x0004, 0x17ec: 0x0004, 0x17ed: 0x0004, 0x17ee: 0x0004, 0x17ef: 0x0004, - 0x17f0: 0x0008, - 0x17fd: 0x0008, + 0x17e0: 0x0024, 0x17e1: 0x0024, 0x17e2: 0x0024, 0x17e3: 0x0024, + 0x17e4: 0x0024, 0x17e5: 0x0024, 0x17e6: 0x0024, 0x17e7: 0x0024, 0x17e8: 0x0024, 0x17e9: 0x0024, + 0x17ea: 0x0024, 0x17eb: 0x0024, 0x17ec: 0x0024, 0x17ed: 0x0024, 0x17ee: 0x0024, 0x17ef: 0x0024, + 0x17f0: 0x0024, 0x17f1: 0x0024, 0x17f2: 0x0024, 0x17f3: 0x0024, 0x17f4: 0x0024, 0x17f5: 0x0024, + 0x17f6: 0x0024, 0x17f7: 0x0024, 0x17f8: 0x0024, 0x17f9: 0x0024, 0x17fa: 0x0024, 0x17fb: 0x0024, + 0x17fc: 0x0024, 0x17fd: 0x0024, 0x17fe: 0x0024, 0x17ff: 0x0024, // Block 0x60, offset 0x1800 - 0x1819: 0x0004, 0x181a: 0x0004, + 0x182a: 0x0024, 0x182b: 0x0024, 0x182c: 0x0024, 0x182d: 0x0024, 0x182e: 0x0024, 0x182f: 0x0024, + 0x1830: 0x0008, + 0x183d: 0x0008, // Block 0x61, offset 0x1840 - 0x1857: 0x0008, - 0x1859: 0x0008, + 0x1859: 0x0024, 0x185a: 0x0024, // Block 0x62, offset 0x1880 - 0x18af: 0x0004, - 0x18b0: 0x0004, 0x18b1: 0x0004, 0x18b2: 0x0004, 0x18b4: 0x0004, 0x18b5: 0x0004, - 0x18b6: 0x0004, 0x18b7: 0x0004, 0x18b8: 0x0004, 0x18b9: 0x0004, 0x18ba: 0x0004, 0x18bb: 0x0004, - 0x18bc: 0x0004, 0x18bd: 0x0004, + 0x1897: 0x0008, + 0x1899: 0x0008, // Block 0x63, offset 0x18c0 - 0x18de: 0x0004, 0x18df: 0x0004, + 0x18ef: 0x0024, + 0x18f0: 0x0024, 0x18f1: 0x0024, 0x18f2: 0x0024, 0x18f4: 0x0024, 0x18f5: 0x0024, + 0x18f6: 0x0024, 0x18f7: 0x0024, 0x18f8: 0x0024, 0x18f9: 0x0024, 0x18fa: 0x0024, 0x18fb: 0x0024, + 0x18fc: 0x0024, 0x18fd: 0x0024, // Block 0x64, offset 0x1900 - 0x1930: 0x0004, 0x1931: 0x0004, + 0x191e: 0x0024, 0x191f: 0x0024, // Block 0x65, offset 0x1940 - 0x1942: 0x0004, - 0x1946: 0x0004, 0x194b: 0x0004, - 0x1963: 0x0400, - 0x1964: 0x0400, 0x1965: 0x0004, 0x1966: 0x0004, 0x1967: 0x0400, - 0x196c: 0x0004, + 0x1970: 0x0024, 0x1971: 0x0024, // Block 0x66, offset 0x1980 - 0x1980: 0x0400, 0x1981: 0x0400, - 0x19b4: 0x0400, 0x19b5: 0x0400, - 0x19b6: 0x0400, 0x19b7: 0x0400, 0x19b8: 0x0400, 0x19b9: 0x0400, 0x19ba: 0x0400, 0x19bb: 0x0400, - 0x19bc: 0x0400, 0x19bd: 0x0400, 0x19be: 0x0400, 0x19bf: 0x0400, + 0x1982: 0x0024, + 0x1986: 0x0024, 0x198b: 0x0024, + 0x19a3: 0x2000, + 0x19a4: 0x2000, 0x19a5: 0x0024, 0x19a6: 0x0024, 0x19a7: 0x2000, + 0x19ac: 0x0024, // Block 0x67, offset 0x19c0 - 0x19c0: 0x0400, 0x19c1: 0x0400, 0x19c2: 0x0400, 0x19c3: 0x0400, 0x19c4: 0x0004, 0x19c5: 0x0004, - 0x19e0: 0x0004, 0x19e1: 0x0004, 0x19e2: 0x0004, 0x19e3: 0x0004, - 0x19e4: 0x0004, 0x19e5: 0x0004, 0x19e6: 0x0004, 0x19e7: 0x0004, 0x19e8: 0x0004, 0x19e9: 0x0004, - 0x19ea: 0x0004, 0x19eb: 0x0004, 0x19ec: 0x0004, 0x19ed: 0x0004, 0x19ee: 0x0004, 0x19ef: 0x0004, - 0x19f0: 0x0004, 0x19f1: 0x0004, - 0x19ff: 0x0004, + 0x19c0: 0x2000, 0x19c1: 0x2000, + 0x19f4: 0x2000, 0x19f5: 0x2000, + 0x19f6: 0x2000, 0x19f7: 0x2000, 0x19f8: 0x2000, 0x19f9: 0x2000, 0x19fa: 0x2000, 0x19fb: 0x2000, + 0x19fc: 0x2000, 0x19fd: 0x2000, 0x19fe: 0x2000, 0x19ff: 0x2000, // Block 0x68, offset 0x1a00 - 0x1a26: 0x0004, 0x1a27: 0x0004, 0x1a28: 0x0004, 0x1a29: 0x0004, - 0x1a2a: 0x0004, 0x1a2b: 0x0004, 0x1a2c: 0x0004, 0x1a2d: 0x0004, + 0x1a00: 0x2000, 0x1a01: 0x2000, 0x1a02: 0x2000, 0x1a03: 0x2000, 0x1a04: 0x0024, 0x1a05: 0x0024, + 0x1a20: 0x0024, 0x1a21: 0x0024, 0x1a22: 0x0024, 0x1a23: 0x0024, + 0x1a24: 0x0024, 0x1a25: 0x0024, 0x1a26: 0x0024, 0x1a27: 0x0024, 0x1a28: 0x0024, 0x1a29: 0x0024, + 0x1a2a: 0x0024, 0x1a2b: 0x0024, 0x1a2c: 0x0024, 0x1a2d: 0x0024, 0x1a2e: 0x0024, 0x1a2f: 0x0024, + 0x1a30: 0x0024, 0x1a31: 0x0024, + 0x1a3f: 0x0024, // Block 0x69, offset 0x1a40 - 0x1a47: 0x0004, 0x1a48: 0x0004, 0x1a49: 0x0004, 0x1a4a: 0x0004, 0x1a4b: 0x0004, - 0x1a4c: 0x0004, 0x1a4d: 0x0004, 0x1a4e: 0x0004, 0x1a4f: 0x0004, 0x1a50: 0x0004, 0x1a51: 0x0004, - 0x1a52: 0x0400, 0x1a53: 0x0400, - 0x1a60: 0x0010, 0x1a61: 0x0010, 0x1a62: 0x0010, 0x1a63: 0x0010, - 0x1a64: 0x0010, 0x1a65: 0x0010, 0x1a66: 0x0010, 0x1a67: 0x0010, 0x1a68: 0x0010, 0x1a69: 0x0010, - 0x1a6a: 0x0010, 0x1a6b: 0x0010, 0x1a6c: 0x0010, 0x1a6d: 0x0010, 0x1a6e: 0x0010, 0x1a6f: 0x0010, - 0x1a70: 0x0010, 0x1a71: 0x0010, 0x1a72: 0x0010, 0x1a73: 0x0010, 0x1a74: 0x0010, 0x1a75: 0x0010, - 0x1a76: 0x0010, 0x1a77: 0x0010, 0x1a78: 0x0010, 0x1a79: 0x0010, 0x1a7a: 0x0010, 0x1a7b: 0x0010, - 0x1a7c: 0x0010, + 0x1a66: 0x0024, 0x1a67: 0x0024, 0x1a68: 0x0024, 0x1a69: 0x0024, + 0x1a6a: 0x0024, 0x1a6b: 0x0024, 0x1a6c: 0x0024, 0x1a6d: 0x0024, // Block 0x6a, offset 0x1a80 - 0x1a80: 0x0004, 0x1a81: 0x0004, 0x1a82: 0x0004, 0x1a83: 0x0400, - 0x1ab3: 0x0004, 0x1ab4: 0x0400, 0x1ab5: 0x0400, - 0x1ab6: 0x0004, 0x1ab7: 0x0004, 0x1ab8: 0x0004, 0x1ab9: 0x0004, 0x1aba: 0x0400, 0x1abb: 0x0400, - 0x1abc: 0x0004, 0x1abd: 0x0004, 0x1abe: 0x0400, 0x1abf: 0x0400, + 0x1a87: 0x0024, 0x1a88: 0x0024, 0x1a89: 0x0024, 0x1a8a: 0x0024, 0x1a8b: 0x0024, + 0x1a8c: 0x0024, 0x1a8d: 0x0024, 0x1a8e: 0x0024, 0x1a8f: 0x0024, 0x1a90: 0x0024, 0x1a91: 0x0024, + 0x1a92: 0x2000, 0x1a93: 0x0024, + 0x1aa0: 0x0080, 0x1aa1: 0x0080, 0x1aa2: 0x0080, 0x1aa3: 0x0080, + 0x1aa4: 0x0080, 0x1aa5: 0x0080, 0x1aa6: 0x0080, 0x1aa7: 0x0080, 0x1aa8: 0x0080, 0x1aa9: 0x0080, + 0x1aaa: 0x0080, 0x1aab: 0x0080, 0x1aac: 0x0080, 0x1aad: 0x0080, 0x1aae: 0x0080, 0x1aaf: 0x0080, + 0x1ab0: 0x0080, 0x1ab1: 0x0080, 0x1ab2: 0x0080, 0x1ab3: 0x0080, 0x1ab4: 0x0080, 0x1ab5: 0x0080, + 0x1ab6: 0x0080, 0x1ab7: 0x0080, 0x1ab8: 0x0080, 0x1ab9: 0x0080, 0x1aba: 0x0080, 0x1abb: 0x0080, + 0x1abc: 0x0080, // Block 0x6b, offset 0x1ac0 - 0x1ac0: 0x0400, - 0x1ae5: 0x0004, + 0x1ac0: 0x0024, 0x1ac1: 0x0024, 0x1ac2: 0x0024, 0x1ac3: 0x2000, + 0x1ac9: 0x0010, 0x1aca: 0x0010, 0x1acb: 0x0010, + 0x1acf: 0x0010, 0x1ad0: 0x0010, 0x1ad1: 0x0010, + 0x1ad2: 0x0010, 0x1ad3: 0x0010, 0x1ad4: 0x0010, 0x1ad5: 0x0010, 0x1ad6: 0x0010, 0x1ad7: 0x0010, + 0x1ad8: 0x0010, 0x1ad9: 0x0010, 0x1ada: 0x0010, 0x1adb: 0x0010, 0x1adc: 0x0010, 0x1add: 0x0010, + 0x1ade: 0x0010, 0x1adf: 0x0010, 0x1ae0: 0x0010, 0x1ae1: 0x0010, 0x1ae2: 0x0010, 0x1ae3: 0x0010, + 0x1ae4: 0x0010, 0x1ae5: 0x0010, 0x1ae6: 0x0010, 0x1ae7: 0x0010, 0x1ae8: 0x0010, 0x1ae9: 0x0010, + 0x1aea: 0x0010, 0x1aeb: 0x0010, 0x1aec: 0x0010, 0x1aed: 0x0010, 0x1aee: 0x0010, 0x1aef: 0x0010, + 0x1af0: 0x0010, 0x1af1: 0x0010, 0x1af2: 0x0010, 0x1af3: 0x0024, 0x1af4: 0x2000, 0x1af5: 0x2000, + 0x1af6: 0x0024, 0x1af7: 0x0024, 0x1af8: 0x0024, 0x1af9: 0x0024, 0x1afa: 0x2000, 0x1afb: 0x2000, + 0x1afc: 0x0024, 0x1afd: 0x0024, 0x1afe: 0x2000, 0x1aff: 0x2000, // Block 0x6c, offset 0x1b00 - 0x1b29: 0x0004, - 0x1b2a: 0x0004, 0x1b2b: 0x0004, 0x1b2c: 0x0004, 0x1b2d: 0x0004, 0x1b2e: 0x0004, 0x1b2f: 0x0400, - 0x1b30: 0x0400, 0x1b31: 0x0004, 0x1b32: 0x0004, 0x1b33: 0x0400, 0x1b34: 0x0400, 0x1b35: 0x0004, - 0x1b36: 0x0004, + 0x1b00: 0x0044, + 0x1b20: 0x0010, 0x1b21: 0x0010, 0x1b22: 0x0010, 0x1b23: 0x0010, + 0x1b24: 0x0010, 0x1b25: 0x0024, 0x1b27: 0x0010, 0x1b28: 0x0010, 0x1b29: 0x0010, + 0x1b2a: 0x0010, 0x1b2b: 0x0010, 0x1b2c: 0x0010, 0x1b2d: 0x0010, 0x1b2e: 0x0010, 0x1b2f: 0x0010, + 0x1b3a: 0x0010, 0x1b3b: 0x0010, + 0x1b3c: 0x0010, 0x1b3d: 0x0010, 0x1b3e: 0x0010, // Block 0x6d, offset 0x1b40 - 0x1b43: 0x0004, - 0x1b4c: 0x0004, 0x1b4d: 0x0400, - 0x1b7c: 0x0004, + 0x1b69: 0x0024, + 0x1b6a: 0x0024, 0x1b6b: 0x0024, 0x1b6c: 0x0024, 0x1b6d: 0x0024, 0x1b6e: 0x0024, 0x1b6f: 0x2000, + 0x1b70: 0x2000, 0x1b71: 0x0024, 0x1b72: 0x0024, 0x1b73: 0x2000, 0x1b74: 0x2000, 0x1b75: 0x0024, + 0x1b76: 0x0024, // Block 0x6e, offset 0x1b80 - 0x1bb0: 0x0004, 0x1bb2: 0x0004, 0x1bb3: 0x0004, 0x1bb4: 0x0004, - 0x1bb7: 0x0004, 0x1bb8: 0x0004, - 0x1bbe: 0x0004, 0x1bbf: 0x0004, + 0x1b83: 0x0024, + 0x1b8c: 0x0024, 0x1b8d: 0x2000, + 0x1ba0: 0x0010, 0x1ba1: 0x0010, 0x1ba2: 0x0010, 0x1ba3: 0x0010, + 0x1ba4: 0x0010, 0x1ba5: 0x0010, 0x1ba6: 0x0010, 0x1ba7: 0x0010, 0x1ba8: 0x0010, 0x1ba9: 0x0010, + 0x1baa: 0x0010, 0x1bab: 0x0010, 0x1bac: 0x0010, 0x1bad: 0x0010, 0x1bae: 0x0010, 0x1baf: 0x0010, + 0x1bb1: 0x0010, 0x1bb2: 0x0010, 0x1bb3: 0x0010, + 0x1bba: 0x0010, + 0x1bbc: 0x0024, 0x1bbe: 0x0010, 0x1bbf: 0x0010, // Block 0x6f, offset 0x1bc0 - 0x1bc1: 0x0004, - 0x1beb: 0x0400, 0x1bec: 0x0004, 0x1bed: 0x0004, 0x1bee: 0x0400, 0x1bef: 0x0400, - 0x1bf5: 0x0400, - 0x1bf6: 0x0004, + 0x1bf0: 0x0024, 0x1bf2: 0x0024, 0x1bf3: 0x0024, 0x1bf4: 0x0024, + 0x1bf7: 0x0024, 0x1bf8: 0x0024, + 0x1bfe: 0x0024, 0x1bff: 0x0024, // Block 0x70, offset 0x1c00 - 0x1c23: 0x0400, - 0x1c24: 0x0400, 0x1c25: 0x0004, 0x1c26: 0x0400, 0x1c27: 0x0400, 0x1c28: 0x0004, 0x1c29: 0x0400, - 0x1c2a: 0x0400, 0x1c2c: 0x0400, 0x1c2d: 0x0004, + 0x1c01: 0x0024, + 0x1c20: 0x0010, 0x1c21: 0x0010, 0x1c22: 0x0010, 0x1c23: 0x0010, + 0x1c24: 0x0010, 0x1c25: 0x0010, 0x1c26: 0x0010, 0x1c27: 0x0010, 0x1c28: 0x0010, 0x1c29: 0x0010, + 0x1c2a: 0x0010, 0x1c2b: 0x2000, 0x1c2c: 0x0024, 0x1c2d: 0x0024, 0x1c2e: 0x2000, 0x1c2f: 0x2000, + 0x1c35: 0x2000, + 0x1c36: 0x0044, // Block 0x71, offset 0x1c40 - 0x1c40: 0x0040, 0x1c41: 0x0080, 0x1c42: 0x0080, 0x1c43: 0x0080, 0x1c44: 0x0080, 0x1c45: 0x0080, - 0x1c46: 0x0080, 0x1c47: 0x0080, 0x1c48: 0x0080, 0x1c49: 0x0080, 0x1c4a: 0x0080, 0x1c4b: 0x0080, - 0x1c4c: 0x0080, 0x1c4d: 0x0080, 0x1c4e: 0x0080, 0x1c4f: 0x0080, 0x1c50: 0x0080, 0x1c51: 0x0080, - 0x1c52: 0x0080, 0x1c53: 0x0080, 0x1c54: 0x0080, 0x1c55: 0x0080, 0x1c56: 0x0080, 0x1c57: 0x0080, - 0x1c58: 0x0080, 0x1c59: 0x0080, 0x1c5a: 0x0080, 0x1c5b: 0x0080, 0x1c5c: 0x0040, 0x1c5d: 0x0080, - 0x1c5e: 0x0080, 0x1c5f: 0x0080, 0x1c60: 0x0080, 0x1c61: 0x0080, 0x1c62: 0x0080, 0x1c63: 0x0080, - 0x1c64: 0x0080, 0x1c65: 0x0080, 0x1c66: 0x0080, 0x1c67: 0x0080, 0x1c68: 0x0080, 0x1c69: 0x0080, - 0x1c6a: 0x0080, 0x1c6b: 0x0080, 0x1c6c: 0x0080, 0x1c6d: 0x0080, 0x1c6e: 0x0080, 0x1c6f: 0x0080, - 0x1c70: 0x0080, 0x1c71: 0x0080, 0x1c72: 0x0080, 0x1c73: 0x0080, 0x1c74: 0x0080, 0x1c75: 0x0080, - 0x1c76: 0x0080, 0x1c77: 0x0080, 0x1c78: 0x0040, 0x1c79: 0x0080, 0x1c7a: 0x0080, 0x1c7b: 0x0080, - 0x1c7c: 0x0080, 0x1c7d: 0x0080, 0x1c7e: 0x0080, 0x1c7f: 0x0080, + 0x1c40: 0x0010, 0x1c41: 0x0010, 0x1c42: 0x0010, 0x1c43: 0x0010, 0x1c44: 0x0010, 0x1c45: 0x0010, + 0x1c46: 0x0010, 0x1c47: 0x0010, 0x1c48: 0x0010, 0x1c49: 0x0010, 0x1c4a: 0x0010, 0x1c4b: 0x0010, + 0x1c4c: 0x0010, 0x1c4d: 0x0010, 0x1c4e: 0x0010, 0x1c4f: 0x0010, 0x1c50: 0x0010, 0x1c51: 0x0010, + 0x1c52: 0x0010, 0x1c53: 0x0010, 0x1c54: 0x0010, 0x1c55: 0x0010, 0x1c56: 0x0010, 0x1c57: 0x0010, + 0x1c58: 0x0010, 0x1c59: 0x0010, 0x1c5a: 0x0010, + 0x1c63: 0x2000, + 0x1c64: 0x2000, 0x1c65: 0x0024, 0x1c66: 0x2000, 0x1c67: 0x2000, 0x1c68: 0x0024, 0x1c69: 0x2000, + 0x1c6a: 0x2000, 0x1c6c: 0x2000, 0x1c6d: 0x0024, // Block 0x72, offset 0x1c80 - 0x1c80: 0x0080, 0x1c81: 0x0080, 0x1c82: 0x0080, 0x1c83: 0x0080, 0x1c84: 0x0080, 0x1c85: 0x0080, - 0x1c86: 0x0080, 0x1c87: 0x0080, 0x1c88: 0x0080, 0x1c89: 0x0080, 0x1c8a: 0x0080, 0x1c8b: 0x0080, - 0x1c8c: 0x0080, 0x1c8d: 0x0080, 0x1c8e: 0x0080, 0x1c8f: 0x0080, 0x1c90: 0x0080, 0x1c91: 0x0080, - 0x1c92: 0x0080, 0x1c93: 0x0080, 0x1c94: 0x0040, 0x1c95: 0x0080, 0x1c96: 0x0080, 0x1c97: 0x0080, - 0x1c98: 0x0080, 0x1c99: 0x0080, 0x1c9a: 0x0080, 0x1c9b: 0x0080, 0x1c9c: 0x0080, 0x1c9d: 0x0080, - 0x1c9e: 0x0080, 0x1c9f: 0x0080, 0x1ca0: 0x0080, 0x1ca1: 0x0080, 0x1ca2: 0x0080, 0x1ca3: 0x0080, - 0x1ca4: 0x0080, 0x1ca5: 0x0080, 0x1ca6: 0x0080, 0x1ca7: 0x0080, 0x1ca8: 0x0080, 0x1ca9: 0x0080, - 0x1caa: 0x0080, 0x1cab: 0x0080, 0x1cac: 0x0080, 0x1cad: 0x0080, 0x1cae: 0x0080, 0x1caf: 0x0080, - 0x1cb0: 0x0040, 0x1cb1: 0x0080, 0x1cb2: 0x0080, 0x1cb3: 0x0080, 0x1cb4: 0x0080, 0x1cb5: 0x0080, - 0x1cb6: 0x0080, 0x1cb7: 0x0080, 0x1cb8: 0x0080, 0x1cb9: 0x0080, 0x1cba: 0x0080, 0x1cbb: 0x0080, - 0x1cbc: 0x0080, 0x1cbd: 0x0080, 0x1cbe: 0x0080, 0x1cbf: 0x0080, + 0x1c80: 0x0200, 0x1c81: 0x0400, 0x1c82: 0x0400, 0x1c83: 0x0400, 0x1c84: 0x0400, 0x1c85: 0x0400, + 0x1c86: 0x0400, 0x1c87: 0x0400, 0x1c88: 0x0400, 0x1c89: 0x0400, 0x1c8a: 0x0400, 0x1c8b: 0x0400, + 0x1c8c: 0x0400, 0x1c8d: 0x0400, 0x1c8e: 0x0400, 0x1c8f: 0x0400, 0x1c90: 0x0400, 0x1c91: 0x0400, + 0x1c92: 0x0400, 0x1c93: 0x0400, 0x1c94: 0x0400, 0x1c95: 0x0400, 0x1c96: 0x0400, 0x1c97: 0x0400, + 0x1c98: 0x0400, 0x1c99: 0x0400, 0x1c9a: 0x0400, 0x1c9b: 0x0400, 0x1c9c: 0x0200, 0x1c9d: 0x0400, + 0x1c9e: 0x0400, 0x1c9f: 0x0400, 0x1ca0: 0x0400, 0x1ca1: 0x0400, 0x1ca2: 0x0400, 0x1ca3: 0x0400, + 0x1ca4: 0x0400, 0x1ca5: 0x0400, 0x1ca6: 0x0400, 0x1ca7: 0x0400, 0x1ca8: 0x0400, 0x1ca9: 0x0400, + 0x1caa: 0x0400, 0x1cab: 0x0400, 0x1cac: 0x0400, 0x1cad: 0x0400, 0x1cae: 0x0400, 0x1caf: 0x0400, + 0x1cb0: 0x0400, 0x1cb1: 0x0400, 0x1cb2: 0x0400, 0x1cb3: 0x0400, 0x1cb4: 0x0400, 0x1cb5: 0x0400, + 0x1cb6: 0x0400, 0x1cb7: 0x0400, 0x1cb8: 0x0200, 0x1cb9: 0x0400, 0x1cba: 0x0400, 0x1cbb: 0x0400, + 0x1cbc: 0x0400, 0x1cbd: 0x0400, 0x1cbe: 0x0400, 0x1cbf: 0x0400, // Block 0x73, offset 0x1cc0 - 0x1cc0: 0x0080, 0x1cc1: 0x0080, 0x1cc2: 0x0080, 0x1cc3: 0x0080, 0x1cc4: 0x0080, 0x1cc5: 0x0080, - 0x1cc6: 0x0080, 0x1cc7: 0x0080, 0x1cc8: 0x0080, 0x1cc9: 0x0080, 0x1cca: 0x0080, 0x1ccb: 0x0080, - 0x1ccc: 0x0040, 0x1ccd: 0x0080, 0x1cce: 0x0080, 0x1ccf: 0x0080, 0x1cd0: 0x0080, 0x1cd1: 0x0080, - 0x1cd2: 0x0080, 0x1cd3: 0x0080, 0x1cd4: 0x0080, 0x1cd5: 0x0080, 0x1cd6: 0x0080, 0x1cd7: 0x0080, - 0x1cd8: 0x0080, 0x1cd9: 0x0080, 0x1cda: 0x0080, 0x1cdb: 0x0080, 0x1cdc: 0x0080, 0x1cdd: 0x0080, - 0x1cde: 0x0080, 0x1cdf: 0x0080, 0x1ce0: 0x0080, 0x1ce1: 0x0080, 0x1ce2: 0x0080, 0x1ce3: 0x0080, - 0x1ce4: 0x0080, 0x1ce5: 0x0080, 0x1ce6: 0x0080, 0x1ce7: 0x0080, 0x1ce8: 0x0040, 0x1ce9: 0x0080, - 0x1cea: 0x0080, 0x1ceb: 0x0080, 0x1cec: 0x0080, 0x1ced: 0x0080, 0x1cee: 0x0080, 0x1cef: 0x0080, - 0x1cf0: 0x0080, 0x1cf1: 0x0080, 0x1cf2: 0x0080, 0x1cf3: 0x0080, 0x1cf4: 0x0080, 0x1cf5: 0x0080, - 0x1cf6: 0x0080, 0x1cf7: 0x0080, 0x1cf8: 0x0080, 0x1cf9: 0x0080, 0x1cfa: 0x0080, 0x1cfb: 0x0080, - 0x1cfc: 0x0080, 0x1cfd: 0x0080, 0x1cfe: 0x0080, 0x1cff: 0x0080, + 0x1cc0: 0x0400, 0x1cc1: 0x0400, 0x1cc2: 0x0400, 0x1cc3: 0x0400, 0x1cc4: 0x0400, 0x1cc5: 0x0400, + 0x1cc6: 0x0400, 0x1cc7: 0x0400, 0x1cc8: 0x0400, 0x1cc9: 0x0400, 0x1cca: 0x0400, 0x1ccb: 0x0400, + 0x1ccc: 0x0400, 0x1ccd: 0x0400, 0x1cce: 0x0400, 0x1ccf: 0x0400, 0x1cd0: 0x0400, 0x1cd1: 0x0400, + 0x1cd2: 0x0400, 0x1cd3: 0x0400, 0x1cd4: 0x0200, 0x1cd5: 0x0400, 0x1cd6: 0x0400, 0x1cd7: 0x0400, + 0x1cd8: 0x0400, 0x1cd9: 0x0400, 0x1cda: 0x0400, 0x1cdb: 0x0400, 0x1cdc: 0x0400, 0x1cdd: 0x0400, + 0x1cde: 0x0400, 0x1cdf: 0x0400, 0x1ce0: 0x0400, 0x1ce1: 0x0400, 0x1ce2: 0x0400, 0x1ce3: 0x0400, + 0x1ce4: 0x0400, 0x1ce5: 0x0400, 0x1ce6: 0x0400, 0x1ce7: 0x0400, 0x1ce8: 0x0400, 0x1ce9: 0x0400, + 0x1cea: 0x0400, 0x1ceb: 0x0400, 0x1cec: 0x0400, 0x1ced: 0x0400, 0x1cee: 0x0400, 0x1cef: 0x0400, + 0x1cf0: 0x0200, 0x1cf1: 0x0400, 0x1cf2: 0x0400, 0x1cf3: 0x0400, 0x1cf4: 0x0400, 0x1cf5: 0x0400, + 0x1cf6: 0x0400, 0x1cf7: 0x0400, 0x1cf8: 0x0400, 0x1cf9: 0x0400, 0x1cfa: 0x0400, 0x1cfb: 0x0400, + 0x1cfc: 0x0400, 0x1cfd: 0x0400, 0x1cfe: 0x0400, 0x1cff: 0x0400, // Block 0x74, offset 0x1d00 - 0x1d00: 0x0080, 0x1d01: 0x0080, 0x1d02: 0x0080, 0x1d03: 0x0080, 0x1d04: 0x0040, 0x1d05: 0x0080, - 0x1d06: 0x0080, 0x1d07: 0x0080, 0x1d08: 0x0080, 0x1d09: 0x0080, 0x1d0a: 0x0080, 0x1d0b: 0x0080, - 0x1d0c: 0x0080, 0x1d0d: 0x0080, 0x1d0e: 0x0080, 0x1d0f: 0x0080, 0x1d10: 0x0080, 0x1d11: 0x0080, - 0x1d12: 0x0080, 0x1d13: 0x0080, 0x1d14: 0x0080, 0x1d15: 0x0080, 0x1d16: 0x0080, 0x1d17: 0x0080, - 0x1d18: 0x0080, 0x1d19: 0x0080, 0x1d1a: 0x0080, 0x1d1b: 0x0080, 0x1d1c: 0x0080, 0x1d1d: 0x0080, - 0x1d1e: 0x0080, 0x1d1f: 0x0080, 0x1d20: 0x0040, 0x1d21: 0x0080, 0x1d22: 0x0080, 0x1d23: 0x0080, - 0x1d24: 0x0080, 0x1d25: 0x0080, 0x1d26: 0x0080, 0x1d27: 0x0080, 0x1d28: 0x0080, 0x1d29: 0x0080, - 0x1d2a: 0x0080, 0x1d2b: 0x0080, 0x1d2c: 0x0080, 0x1d2d: 0x0080, 0x1d2e: 0x0080, 0x1d2f: 0x0080, - 0x1d30: 0x0080, 0x1d31: 0x0080, 0x1d32: 0x0080, 0x1d33: 0x0080, 0x1d34: 0x0080, 0x1d35: 0x0080, - 0x1d36: 0x0080, 0x1d37: 0x0080, 0x1d38: 0x0080, 0x1d39: 0x0080, 0x1d3a: 0x0080, 0x1d3b: 0x0080, - 0x1d3c: 0x0040, 0x1d3d: 0x0080, 0x1d3e: 0x0080, 0x1d3f: 0x0080, + 0x1d00: 0x0400, 0x1d01: 0x0400, 0x1d02: 0x0400, 0x1d03: 0x0400, 0x1d04: 0x0400, 0x1d05: 0x0400, + 0x1d06: 0x0400, 0x1d07: 0x0400, 0x1d08: 0x0400, 0x1d09: 0x0400, 0x1d0a: 0x0400, 0x1d0b: 0x0400, + 0x1d0c: 0x0200, 0x1d0d: 0x0400, 0x1d0e: 0x0400, 0x1d0f: 0x0400, 0x1d10: 0x0400, 0x1d11: 0x0400, + 0x1d12: 0x0400, 0x1d13: 0x0400, 0x1d14: 0x0400, 0x1d15: 0x0400, 0x1d16: 0x0400, 0x1d17: 0x0400, + 0x1d18: 0x0400, 0x1d19: 0x0400, 0x1d1a: 0x0400, 0x1d1b: 0x0400, 0x1d1c: 0x0400, 0x1d1d: 0x0400, + 0x1d1e: 0x0400, 0x1d1f: 0x0400, 0x1d20: 0x0400, 0x1d21: 0x0400, 0x1d22: 0x0400, 0x1d23: 0x0400, + 0x1d24: 0x0400, 0x1d25: 0x0400, 0x1d26: 0x0400, 0x1d27: 0x0400, 0x1d28: 0x0200, 0x1d29: 0x0400, + 0x1d2a: 0x0400, 0x1d2b: 0x0400, 0x1d2c: 0x0400, 0x1d2d: 0x0400, 0x1d2e: 0x0400, 0x1d2f: 0x0400, + 0x1d30: 0x0400, 0x1d31: 0x0400, 0x1d32: 0x0400, 0x1d33: 0x0400, 0x1d34: 0x0400, 0x1d35: 0x0400, + 0x1d36: 0x0400, 0x1d37: 0x0400, 0x1d38: 0x0400, 0x1d39: 0x0400, 0x1d3a: 0x0400, 0x1d3b: 0x0400, + 0x1d3c: 0x0400, 0x1d3d: 0x0400, 0x1d3e: 0x0400, 0x1d3f: 0x0400, // Block 0x75, offset 0x1d40 - 0x1d40: 0x0080, 0x1d41: 0x0080, 0x1d42: 0x0080, 0x1d43: 0x0080, 0x1d44: 0x0080, 0x1d45: 0x0080, - 0x1d46: 0x0080, 0x1d47: 0x0080, 0x1d48: 0x0080, 0x1d49: 0x0080, 0x1d4a: 0x0080, 0x1d4b: 0x0080, - 0x1d4c: 0x0080, 0x1d4d: 0x0080, 0x1d4e: 0x0080, 0x1d4f: 0x0080, 0x1d50: 0x0080, 0x1d51: 0x0080, - 0x1d52: 0x0080, 0x1d53: 0x0080, 0x1d54: 0x0080, 0x1d55: 0x0080, 0x1d56: 0x0080, 0x1d57: 0x0080, - 0x1d58: 0x0040, 0x1d59: 0x0080, 0x1d5a: 0x0080, 0x1d5b: 0x0080, 0x1d5c: 0x0080, 0x1d5d: 0x0080, - 0x1d5e: 0x0080, 0x1d5f: 0x0080, 0x1d60: 0x0080, 0x1d61: 0x0080, 0x1d62: 0x0080, 0x1d63: 0x0080, - 0x1d64: 0x0080, 0x1d65: 0x0080, 0x1d66: 0x0080, 0x1d67: 0x0080, 0x1d68: 0x0080, 0x1d69: 0x0080, - 0x1d6a: 0x0080, 0x1d6b: 0x0080, 0x1d6c: 0x0080, 0x1d6d: 0x0080, 0x1d6e: 0x0080, 0x1d6f: 0x0080, - 0x1d70: 0x0080, 0x1d71: 0x0080, 0x1d72: 0x0080, 0x1d73: 0x0080, 0x1d74: 0x0040, 0x1d75: 0x0080, - 0x1d76: 0x0080, 0x1d77: 0x0080, 0x1d78: 0x0080, 0x1d79: 0x0080, 0x1d7a: 0x0080, 0x1d7b: 0x0080, - 0x1d7c: 0x0080, 0x1d7d: 0x0080, 0x1d7e: 0x0080, 0x1d7f: 0x0080, + 0x1d40: 0x0400, 0x1d41: 0x0400, 0x1d42: 0x0400, 0x1d43: 0x0400, 0x1d44: 0x0200, 0x1d45: 0x0400, + 0x1d46: 0x0400, 0x1d47: 0x0400, 0x1d48: 0x0400, 0x1d49: 0x0400, 0x1d4a: 0x0400, 0x1d4b: 0x0400, + 0x1d4c: 0x0400, 0x1d4d: 0x0400, 0x1d4e: 0x0400, 0x1d4f: 0x0400, 0x1d50: 0x0400, 0x1d51: 0x0400, + 0x1d52: 0x0400, 0x1d53: 0x0400, 0x1d54: 0x0400, 0x1d55: 0x0400, 0x1d56: 0x0400, 0x1d57: 0x0400, + 0x1d58: 0x0400, 0x1d59: 0x0400, 0x1d5a: 0x0400, 0x1d5b: 0x0400, 0x1d5c: 0x0400, 0x1d5d: 0x0400, + 0x1d5e: 0x0400, 0x1d5f: 0x0400, 0x1d60: 0x0200, 0x1d61: 0x0400, 0x1d62: 0x0400, 0x1d63: 0x0400, + 0x1d64: 0x0400, 0x1d65: 0x0400, 0x1d66: 0x0400, 0x1d67: 0x0400, 0x1d68: 0x0400, 0x1d69: 0x0400, + 0x1d6a: 0x0400, 0x1d6b: 0x0400, 0x1d6c: 0x0400, 0x1d6d: 0x0400, 0x1d6e: 0x0400, 0x1d6f: 0x0400, + 0x1d70: 0x0400, 0x1d71: 0x0400, 0x1d72: 0x0400, 0x1d73: 0x0400, 0x1d74: 0x0400, 0x1d75: 0x0400, + 0x1d76: 0x0400, 0x1d77: 0x0400, 0x1d78: 0x0400, 0x1d79: 0x0400, 0x1d7a: 0x0400, 0x1d7b: 0x0400, + 0x1d7c: 0x0200, 0x1d7d: 0x0400, 0x1d7e: 0x0400, 0x1d7f: 0x0400, // Block 0x76, offset 0x1d80 - 0x1d80: 0x0080, 0x1d81: 0x0080, 0x1d82: 0x0080, 0x1d83: 0x0080, 0x1d84: 0x0080, 0x1d85: 0x0080, - 0x1d86: 0x0080, 0x1d87: 0x0080, 0x1d88: 0x0080, 0x1d89: 0x0080, 0x1d8a: 0x0080, 0x1d8b: 0x0080, - 0x1d8c: 0x0080, 0x1d8d: 0x0080, 0x1d8e: 0x0080, 0x1d8f: 0x0080, 0x1d90: 0x0040, 0x1d91: 0x0080, - 0x1d92: 0x0080, 0x1d93: 0x0080, 0x1d94: 0x0080, 0x1d95: 0x0080, 0x1d96: 0x0080, 0x1d97: 0x0080, - 0x1d98: 0x0080, 0x1d99: 0x0080, 0x1d9a: 0x0080, 0x1d9b: 0x0080, 0x1d9c: 0x0080, 0x1d9d: 0x0080, - 0x1d9e: 0x0080, 0x1d9f: 0x0080, 0x1da0: 0x0080, 0x1da1: 0x0080, 0x1da2: 0x0080, 0x1da3: 0x0080, - 0x1da4: 0x0080, 0x1da5: 0x0080, 0x1da6: 0x0080, 0x1da7: 0x0080, 0x1da8: 0x0080, 0x1da9: 0x0080, - 0x1daa: 0x0080, 0x1dab: 0x0080, 0x1dac: 0x0040, 0x1dad: 0x0080, 0x1dae: 0x0080, 0x1daf: 0x0080, - 0x1db0: 0x0080, 0x1db1: 0x0080, 0x1db2: 0x0080, 0x1db3: 0x0080, 0x1db4: 0x0080, 0x1db5: 0x0080, - 0x1db6: 0x0080, 0x1db7: 0x0080, 0x1db8: 0x0080, 0x1db9: 0x0080, 0x1dba: 0x0080, 0x1dbb: 0x0080, - 0x1dbc: 0x0080, 0x1dbd: 0x0080, 0x1dbe: 0x0080, 0x1dbf: 0x0080, + 0x1d80: 0x0400, 0x1d81: 0x0400, 0x1d82: 0x0400, 0x1d83: 0x0400, 0x1d84: 0x0400, 0x1d85: 0x0400, + 0x1d86: 0x0400, 0x1d87: 0x0400, 0x1d88: 0x0400, 0x1d89: 0x0400, 0x1d8a: 0x0400, 0x1d8b: 0x0400, + 0x1d8c: 0x0400, 0x1d8d: 0x0400, 0x1d8e: 0x0400, 0x1d8f: 0x0400, 0x1d90: 0x0400, 0x1d91: 0x0400, + 0x1d92: 0x0400, 0x1d93: 0x0400, 0x1d94: 0x0400, 0x1d95: 0x0400, 0x1d96: 0x0400, 0x1d97: 0x0400, + 0x1d98: 0x0200, 0x1d99: 0x0400, 0x1d9a: 0x0400, 0x1d9b: 0x0400, 0x1d9c: 0x0400, 0x1d9d: 0x0400, + 0x1d9e: 0x0400, 0x1d9f: 0x0400, 0x1da0: 0x0400, 0x1da1: 0x0400, 0x1da2: 0x0400, 0x1da3: 0x0400, + 0x1da4: 0x0400, 0x1da5: 0x0400, 0x1da6: 0x0400, 0x1da7: 0x0400, 0x1da8: 0x0400, 0x1da9: 0x0400, + 0x1daa: 0x0400, 0x1dab: 0x0400, 0x1dac: 0x0400, 0x1dad: 0x0400, 0x1dae: 0x0400, 0x1daf: 0x0400, + 0x1db0: 0x0400, 0x1db1: 0x0400, 0x1db2: 0x0400, 0x1db3: 0x0400, 0x1db4: 0x0200, 0x1db5: 0x0400, + 0x1db6: 0x0400, 0x1db7: 0x0400, 0x1db8: 0x0400, 0x1db9: 0x0400, 0x1dba: 0x0400, 0x1dbb: 0x0400, + 0x1dbc: 0x0400, 0x1dbd: 0x0400, 0x1dbe: 0x0400, 0x1dbf: 0x0400, // Block 0x77, offset 0x1dc0 - 0x1dc0: 0x0080, 0x1dc1: 0x0080, 0x1dc2: 0x0080, 0x1dc3: 0x0080, 0x1dc4: 0x0080, 0x1dc5: 0x0080, - 0x1dc6: 0x0080, 0x1dc7: 0x0080, 0x1dc8: 0x0040, 0x1dc9: 0x0080, 0x1dca: 0x0080, 0x1dcb: 0x0080, - 0x1dcc: 0x0080, 0x1dcd: 0x0080, 0x1dce: 0x0080, 0x1dcf: 0x0080, 0x1dd0: 0x0080, 0x1dd1: 0x0080, - 0x1dd2: 0x0080, 0x1dd3: 0x0080, 0x1dd4: 0x0080, 0x1dd5: 0x0080, 0x1dd6: 0x0080, 0x1dd7: 0x0080, - 0x1dd8: 0x0080, 0x1dd9: 0x0080, 0x1dda: 0x0080, 0x1ddb: 0x0080, 0x1ddc: 0x0080, 0x1ddd: 0x0080, - 0x1dde: 0x0080, 0x1ddf: 0x0080, 0x1de0: 0x0080, 0x1de1: 0x0080, 0x1de2: 0x0080, 0x1de3: 0x0080, - 0x1de4: 0x0040, 0x1de5: 0x0080, 0x1de6: 0x0080, 0x1de7: 0x0080, 0x1de8: 0x0080, 0x1de9: 0x0080, - 0x1dea: 0x0080, 0x1deb: 0x0080, 0x1dec: 0x0080, 0x1ded: 0x0080, 0x1dee: 0x0080, 0x1def: 0x0080, - 0x1df0: 0x0080, 0x1df1: 0x0080, 0x1df2: 0x0080, 0x1df3: 0x0080, 0x1df4: 0x0080, 0x1df5: 0x0080, - 0x1df6: 0x0080, 0x1df7: 0x0080, 0x1df8: 0x0080, 0x1df9: 0x0080, 0x1dfa: 0x0080, 0x1dfb: 0x0080, - 0x1dfc: 0x0080, 0x1dfd: 0x0080, 0x1dfe: 0x0080, 0x1dff: 0x0080, + 0x1dc0: 0x0400, 0x1dc1: 0x0400, 0x1dc2: 0x0400, 0x1dc3: 0x0400, 0x1dc4: 0x0400, 0x1dc5: 0x0400, + 0x1dc6: 0x0400, 0x1dc7: 0x0400, 0x1dc8: 0x0400, 0x1dc9: 0x0400, 0x1dca: 0x0400, 0x1dcb: 0x0400, + 0x1dcc: 0x0400, 0x1dcd: 0x0400, 0x1dce: 0x0400, 0x1dcf: 0x0400, 0x1dd0: 0x0200, 0x1dd1: 0x0400, + 0x1dd2: 0x0400, 0x1dd3: 0x0400, 0x1dd4: 0x0400, 0x1dd5: 0x0400, 0x1dd6: 0x0400, 0x1dd7: 0x0400, + 0x1dd8: 0x0400, 0x1dd9: 0x0400, 0x1dda: 0x0400, 0x1ddb: 0x0400, 0x1ddc: 0x0400, 0x1ddd: 0x0400, + 0x1dde: 0x0400, 0x1ddf: 0x0400, 0x1de0: 0x0400, 0x1de1: 0x0400, 0x1de2: 0x0400, 0x1de3: 0x0400, + 0x1de4: 0x0400, 0x1de5: 0x0400, 0x1de6: 0x0400, 0x1de7: 0x0400, 0x1de8: 0x0400, 0x1de9: 0x0400, + 0x1dea: 0x0400, 0x1deb: 0x0400, 0x1dec: 0x0200, 0x1ded: 0x0400, 0x1dee: 0x0400, 0x1def: 0x0400, + 0x1df0: 0x0400, 0x1df1: 0x0400, 0x1df2: 0x0400, 0x1df3: 0x0400, 0x1df4: 0x0400, 0x1df5: 0x0400, + 0x1df6: 0x0400, 0x1df7: 0x0400, 0x1df8: 0x0400, 0x1df9: 0x0400, 0x1dfa: 0x0400, 0x1dfb: 0x0400, + 0x1dfc: 0x0400, 0x1dfd: 0x0400, 0x1dfe: 0x0400, 0x1dff: 0x0400, // Block 0x78, offset 0x1e00 - 0x1e00: 0x0080, 0x1e01: 0x0080, 0x1e02: 0x0080, 0x1e03: 0x0080, 0x1e04: 0x0080, 0x1e05: 0x0080, - 0x1e06: 0x0080, 0x1e07: 0x0080, 0x1e08: 0x0040, 0x1e09: 0x0080, 0x1e0a: 0x0080, 0x1e0b: 0x0080, - 0x1e0c: 0x0080, 0x1e0d: 0x0080, 0x1e0e: 0x0080, 0x1e0f: 0x0080, 0x1e10: 0x0080, 0x1e11: 0x0080, - 0x1e12: 0x0080, 0x1e13: 0x0080, 0x1e14: 0x0080, 0x1e15: 0x0080, 0x1e16: 0x0080, 0x1e17: 0x0080, - 0x1e18: 0x0080, 0x1e19: 0x0080, 0x1e1a: 0x0080, 0x1e1b: 0x0080, 0x1e1c: 0x0080, 0x1e1d: 0x0080, - 0x1e1e: 0x0080, 0x1e1f: 0x0080, 0x1e20: 0x0080, 0x1e21: 0x0080, 0x1e22: 0x0080, 0x1e23: 0x0080, - 0x1e30: 0x1000, 0x1e31: 0x1000, 0x1e32: 0x1000, 0x1e33: 0x1000, 0x1e34: 0x1000, 0x1e35: 0x1000, - 0x1e36: 0x1000, 0x1e37: 0x1000, 0x1e38: 0x1000, 0x1e39: 0x1000, 0x1e3a: 0x1000, 0x1e3b: 0x1000, - 0x1e3c: 0x1000, 0x1e3d: 0x1000, 0x1e3e: 0x1000, 0x1e3f: 0x1000, + 0x1e00: 0x0400, 0x1e01: 0x0400, 0x1e02: 0x0400, 0x1e03: 0x0400, 0x1e04: 0x0400, 0x1e05: 0x0400, + 0x1e06: 0x0400, 0x1e07: 0x0400, 0x1e08: 0x0200, 0x1e09: 0x0400, 0x1e0a: 0x0400, 0x1e0b: 0x0400, + 0x1e0c: 0x0400, 0x1e0d: 0x0400, 0x1e0e: 0x0400, 0x1e0f: 0x0400, 0x1e10: 0x0400, 0x1e11: 0x0400, + 0x1e12: 0x0400, 0x1e13: 0x0400, 0x1e14: 0x0400, 0x1e15: 0x0400, 0x1e16: 0x0400, 0x1e17: 0x0400, + 0x1e18: 0x0400, 0x1e19: 0x0400, 0x1e1a: 0x0400, 0x1e1b: 0x0400, 0x1e1c: 0x0400, 0x1e1d: 0x0400, + 0x1e1e: 0x0400, 0x1e1f: 0x0400, 0x1e20: 0x0400, 0x1e21: 0x0400, 0x1e22: 0x0400, 0x1e23: 0x0400, + 0x1e24: 0x0200, 0x1e25: 0x0400, 0x1e26: 0x0400, 0x1e27: 0x0400, 0x1e28: 0x0400, 0x1e29: 0x0400, + 0x1e2a: 0x0400, 0x1e2b: 0x0400, 0x1e2c: 0x0400, 0x1e2d: 0x0400, 0x1e2e: 0x0400, 0x1e2f: 0x0400, + 0x1e30: 0x0400, 0x1e31: 0x0400, 0x1e32: 0x0400, 0x1e33: 0x0400, 0x1e34: 0x0400, 0x1e35: 0x0400, + 0x1e36: 0x0400, 0x1e37: 0x0400, 0x1e38: 0x0400, 0x1e39: 0x0400, 0x1e3a: 0x0400, 0x1e3b: 0x0400, + 0x1e3c: 0x0400, 0x1e3d: 0x0400, 0x1e3e: 0x0400, 0x1e3f: 0x0400, // Block 0x79, offset 0x1e40 - 0x1e40: 0x1000, 0x1e41: 0x1000, 0x1e42: 0x1000, 0x1e43: 0x1000, 0x1e44: 0x1000, 0x1e45: 0x1000, - 0x1e46: 0x1000, 0x1e4b: 0x0800, - 0x1e4c: 0x0800, 0x1e4d: 0x0800, 0x1e4e: 0x0800, 0x1e4f: 0x0800, 0x1e50: 0x0800, 0x1e51: 0x0800, - 0x1e52: 0x0800, 0x1e53: 0x0800, 0x1e54: 0x0800, 0x1e55: 0x0800, 0x1e56: 0x0800, 0x1e57: 0x0800, - 0x1e58: 0x0800, 0x1e59: 0x0800, 0x1e5a: 0x0800, 0x1e5b: 0x0800, 0x1e5c: 0x0800, 0x1e5d: 0x0800, - 0x1e5e: 0x0800, 0x1e5f: 0x0800, 0x1e60: 0x0800, 0x1e61: 0x0800, 0x1e62: 0x0800, 0x1e63: 0x0800, - 0x1e64: 0x0800, 0x1e65: 0x0800, 0x1e66: 0x0800, 0x1e67: 0x0800, 0x1e68: 0x0800, 0x1e69: 0x0800, - 0x1e6a: 0x0800, 0x1e6b: 0x0800, 0x1e6c: 0x0800, 0x1e6d: 0x0800, 0x1e6e: 0x0800, 0x1e6f: 0x0800, - 0x1e70: 0x0800, 0x1e71: 0x0800, 0x1e72: 0x0800, 0x1e73: 0x0800, 0x1e74: 0x0800, 0x1e75: 0x0800, - 0x1e76: 0x0800, 0x1e77: 0x0800, 0x1e78: 0x0800, 0x1e79: 0x0800, 0x1e7a: 0x0800, 0x1e7b: 0x0800, + 0x1e40: 0x0400, 0x1e41: 0x0400, 0x1e42: 0x0400, 0x1e43: 0x0400, 0x1e44: 0x0400, 0x1e45: 0x0400, + 0x1e46: 0x0400, 0x1e47: 0x0400, 0x1e48: 0x0200, 0x1e49: 0x0400, 0x1e4a: 0x0400, 0x1e4b: 0x0400, + 0x1e4c: 0x0400, 0x1e4d: 0x0400, 0x1e4e: 0x0400, 0x1e4f: 0x0400, 0x1e50: 0x0400, 0x1e51: 0x0400, + 0x1e52: 0x0400, 0x1e53: 0x0400, 0x1e54: 0x0400, 0x1e55: 0x0400, 0x1e56: 0x0400, 0x1e57: 0x0400, + 0x1e58: 0x0400, 0x1e59: 0x0400, 0x1e5a: 0x0400, 0x1e5b: 0x0400, 0x1e5c: 0x0400, 0x1e5d: 0x0400, + 0x1e5e: 0x0400, 0x1e5f: 0x0400, 0x1e60: 0x0400, 0x1e61: 0x0400, 0x1e62: 0x0400, 0x1e63: 0x0400, + 0x1e70: 0x8000, 0x1e71: 0x8000, 0x1e72: 0x8000, 0x1e73: 0x8000, 0x1e74: 0x8000, 0x1e75: 0x8000, + 0x1e76: 0x8000, 0x1e77: 0x8000, 0x1e78: 0x8000, 0x1e79: 0x8000, 0x1e7a: 0x8000, 0x1e7b: 0x8000, + 0x1e7c: 0x8000, 0x1e7d: 0x8000, 0x1e7e: 0x8000, 0x1e7f: 0x8000, // Block 0x7a, offset 0x1e80 - 0x1e9e: 0x0004, + 0x1e80: 0x8000, 0x1e81: 0x8000, 0x1e82: 0x8000, 0x1e83: 0x8000, 0x1e84: 0x8000, 0x1e85: 0x8000, + 0x1e86: 0x8000, 0x1e8b: 0x4000, + 0x1e8c: 0x4000, 0x1e8d: 0x4000, 0x1e8e: 0x4000, 0x1e8f: 0x4000, 0x1e90: 0x4000, 0x1e91: 0x4000, + 0x1e92: 0x4000, 0x1e93: 0x4000, 0x1e94: 0x4000, 0x1e95: 0x4000, 0x1e96: 0x4000, 0x1e97: 0x4000, + 0x1e98: 0x4000, 0x1e99: 0x4000, 0x1e9a: 0x4000, 0x1e9b: 0x4000, 0x1e9c: 0x4000, 0x1e9d: 0x4000, + 0x1e9e: 0x4000, 0x1e9f: 0x4000, 0x1ea0: 0x4000, 0x1ea1: 0x4000, 0x1ea2: 0x4000, 0x1ea3: 0x4000, + 0x1ea4: 0x4000, 0x1ea5: 0x4000, 0x1ea6: 0x4000, 0x1ea7: 0x4000, 0x1ea8: 0x4000, 0x1ea9: 0x4000, + 0x1eaa: 0x4000, 0x1eab: 0x4000, 0x1eac: 0x4000, 0x1ead: 0x4000, 0x1eae: 0x4000, 0x1eaf: 0x4000, + 0x1eb0: 0x4000, 0x1eb1: 0x4000, 0x1eb2: 0x4000, 0x1eb3: 0x4000, 0x1eb4: 0x4000, 0x1eb5: 0x4000, + 0x1eb6: 0x4000, 0x1eb7: 0x4000, 0x1eb8: 0x4000, 0x1eb9: 0x4000, 0x1eba: 0x4000, 0x1ebb: 0x4000, // Block 0x7b, offset 0x1ec0 - 0x1ec0: 0x0004, 0x1ec1: 0x0004, 0x1ec2: 0x0004, 0x1ec3: 0x0004, 0x1ec4: 0x0004, 0x1ec5: 0x0004, - 0x1ec6: 0x0004, 0x1ec7: 0x0004, 0x1ec8: 0x0004, 0x1ec9: 0x0004, 0x1eca: 0x0004, 0x1ecb: 0x0004, - 0x1ecc: 0x0004, 0x1ecd: 0x0004, 0x1ece: 0x0004, 0x1ecf: 0x0004, - 0x1ee0: 0x0004, 0x1ee1: 0x0004, 0x1ee2: 0x0004, 0x1ee3: 0x0004, - 0x1ee4: 0x0004, 0x1ee5: 0x0004, 0x1ee6: 0x0004, 0x1ee7: 0x0004, 0x1ee8: 0x0004, 0x1ee9: 0x0004, - 0x1eea: 0x0004, 0x1eeb: 0x0004, 0x1eec: 0x0004, 0x1eed: 0x0004, 0x1eee: 0x0004, 0x1eef: 0x0004, + 0x1ede: 0x0024, // Block 0x7c, offset 0x1f00 - 0x1f3f: 0x0002, + 0x1f00: 0x0024, 0x1f01: 0x0024, 0x1f02: 0x0024, 0x1f03: 0x0024, 0x1f04: 0x0024, 0x1f05: 0x0024, + 0x1f06: 0x0024, 0x1f07: 0x0024, 0x1f08: 0x0024, 0x1f09: 0x0024, 0x1f0a: 0x0024, 0x1f0b: 0x0024, + 0x1f0c: 0x0024, 0x1f0d: 0x0024, 0x1f0e: 0x0024, 0x1f0f: 0x0024, + 0x1f20: 0x0024, 0x1f21: 0x0024, 0x1f22: 0x0024, 0x1f23: 0x0024, + 0x1f24: 0x0024, 0x1f25: 0x0024, 0x1f26: 0x0024, 0x1f27: 0x0024, 0x1f28: 0x0024, 0x1f29: 0x0024, + 0x1f2a: 0x0024, 0x1f2b: 0x0024, 0x1f2c: 0x0024, 0x1f2d: 0x0024, 0x1f2e: 0x0024, 0x1f2f: 0x0024, // Block 0x7d, offset 0x1f40 - 0x1f70: 0x0002, 0x1f71: 0x0002, 0x1f72: 0x0002, 0x1f73: 0x0002, 0x1f74: 0x0002, 0x1f75: 0x0002, - 0x1f76: 0x0002, 0x1f77: 0x0002, 0x1f78: 0x0002, 0x1f79: 0x0002, 0x1f7a: 0x0002, 0x1f7b: 0x0002, + 0x1f7f: 0x0002, // Block 0x7e, offset 0x1f80 - 0x1fbd: 0x0004, + 0x1fb0: 0x0002, 0x1fb1: 0x0002, 0x1fb2: 0x0002, 0x1fb3: 0x0002, 0x1fb4: 0x0002, 0x1fb5: 0x0002, + 0x1fb6: 0x0002, 0x1fb7: 0x0002, 0x1fb8: 0x0002, 0x1fb9: 0x0002, 0x1fba: 0x0002, 0x1fbb: 0x0002, // Block 0x7f, offset 0x1fc0 - 0x1fe0: 0x0004, + 0x1ffd: 0x0024, // Block 0x80, offset 0x2000 - 0x2036: 0x0004, 0x2037: 0x0004, 0x2038: 0x0004, 0x2039: 0x0004, 0x203a: 0x0004, + 0x2020: 0x0024, // Block 0x81, offset 0x2040 - 0x2041: 0x0004, 0x2042: 0x0004, 0x2043: 0x0004, 0x2045: 0x0004, - 0x2046: 0x0004, - 0x204c: 0x0004, 0x204d: 0x0004, 0x204e: 0x0004, 0x204f: 0x0004, - 0x2078: 0x0004, 0x2079: 0x0004, 0x207a: 0x0004, - 0x207f: 0x0004, + 0x2076: 0x0024, 0x2077: 0x0024, 0x2078: 0x0024, 0x2079: 0x0024, 0x207a: 0x0024, // Block 0x82, offset 0x2080 - 0x20a5: 0x0004, 0x20a6: 0x0004, + 0x2080: 0x0010, 0x2081: 0x0024, 0x2082: 0x0024, 0x2083: 0x0024, 0x2085: 0x0024, + 0x2086: 0x0024, + 0x208c: 0x0024, 0x208d: 0x0024, 0x208e: 0x0024, 0x208f: 0x0024, 0x2090: 0x0010, 0x2091: 0x0010, + 0x2092: 0x0010, 0x2093: 0x0010, 0x2095: 0x0010, 0x2096: 0x0010, 0x2097: 0x0010, + 0x2099: 0x0010, 0x209a: 0x0010, 0x209b: 0x0010, 0x209c: 0x0010, 0x209d: 0x0010, + 0x209e: 0x0010, 0x209f: 0x0010, 0x20a0: 0x0010, 0x20a1: 0x0010, 0x20a2: 0x0010, 0x20a3: 0x0010, + 0x20a4: 0x0010, 0x20a5: 0x0010, 0x20a6: 0x0010, 0x20a7: 0x0010, 0x20a8: 0x0010, 0x20a9: 0x0010, + 0x20aa: 0x0010, 0x20ab: 0x0010, 0x20ac: 0x0010, 0x20ad: 0x0010, 0x20ae: 0x0010, 0x20af: 0x0010, + 0x20b0: 0x0010, 0x20b1: 0x0010, 0x20b2: 0x0010, 0x20b3: 0x0010, 0x20b4: 0x0010, 0x20b5: 0x0010, + 0x20b8: 0x0024, 0x20b9: 0x0024, 0x20ba: 0x0024, + 0x20bf: 0x0044, // Block 0x83, offset 0x20c0 - 0x20e4: 0x0004, 0x20e5: 0x0004, 0x20e6: 0x0004, 0x20e7: 0x0004, + 0x20e5: 0x0024, 0x20e6: 0x0024, // Block 0x84, offset 0x2100 - 0x212b: 0x0004, 0x212c: 0x0004, + 0x2124: 0x0024, 0x2125: 0x0024, 0x2126: 0x0024, 0x2127: 0x0024, // Block 0x85, offset 0x2140 - 0x217d: 0x0004, 0x217e: 0x0004, 0x217f: 0x0004, + 0x2169: 0x0024, + 0x216a: 0x0024, 0x216b: 0x0024, 0x216c: 0x0024, 0x216d: 0x0024, // Block 0x86, offset 0x2180 - 0x2186: 0x0004, 0x2187: 0x0004, 0x2188: 0x0004, 0x2189: 0x0004, 0x218a: 0x0004, 0x218b: 0x0004, - 0x218c: 0x0004, 0x218d: 0x0004, 0x218e: 0x0004, 0x218f: 0x0004, 0x2190: 0x0004, + 0x21ab: 0x0024, 0x21ac: 0x0024, // Block 0x87, offset 0x21c0 - 0x21c2: 0x0004, 0x21c3: 0x0004, 0x21c4: 0x0004, 0x21c5: 0x0004, + 0x21fa: 0x0024, 0x21fb: 0x0024, + 0x21fc: 0x0024, 0x21fd: 0x0024, 0x21fe: 0x0024, 0x21ff: 0x0024, // Block 0x88, offset 0x2200 - 0x2200: 0x0400, 0x2201: 0x0004, 0x2202: 0x0400, - 0x2238: 0x0004, 0x2239: 0x0004, 0x223a: 0x0004, 0x223b: 0x0004, - 0x223c: 0x0004, 0x223d: 0x0004, 0x223e: 0x0004, 0x223f: 0x0004, + 0x2206: 0x0024, 0x2207: 0x0024, 0x2208: 0x0024, 0x2209: 0x0024, 0x220a: 0x0024, 0x220b: 0x0024, + 0x220c: 0x0024, 0x220d: 0x0024, 0x220e: 0x0024, 0x220f: 0x0024, 0x2210: 0x0024, // Block 0x89, offset 0x2240 - 0x2240: 0x0004, 0x2241: 0x0004, 0x2242: 0x0004, 0x2243: 0x0004, 0x2244: 0x0004, 0x2245: 0x0004, - 0x2246: 0x0004, - 0x2270: 0x0004, 0x2273: 0x0004, 0x2274: 0x0004, - 0x227f: 0x0004, + 0x2242: 0x0024, 0x2243: 0x0024, 0x2244: 0x0024, 0x2245: 0x0024, // Block 0x8a, offset 0x2280 - 0x2280: 0x0004, 0x2281: 0x0004, 0x2282: 0x0400, - 0x22b0: 0x0400, 0x22b1: 0x0400, 0x22b2: 0x0400, 0x22b3: 0x0004, 0x22b4: 0x0004, 0x22b5: 0x0004, - 0x22b6: 0x0004, 0x22b7: 0x0400, 0x22b8: 0x0400, 0x22b9: 0x0004, 0x22ba: 0x0004, - 0x22bd: 0x0100, + 0x2280: 0x2000, 0x2281: 0x0024, 0x2282: 0x2000, + 0x22b8: 0x0024, 0x22b9: 0x0024, 0x22ba: 0x0024, 0x22bb: 0x0024, + 0x22bc: 0x0024, 0x22bd: 0x0024, 0x22be: 0x0024, 0x22bf: 0x0024, // Block 0x8b, offset 0x22c0 - 0x22c2: 0x0004, - 0x22cd: 0x0100, + 0x22c0: 0x0024, 0x22c1: 0x0024, 0x22c2: 0x0024, 0x22c3: 0x0024, 0x22c4: 0x0024, 0x22c5: 0x0024, + 0x22c6: 0x0024, + 0x22f0: 0x0024, 0x22f3: 0x0024, 0x22f4: 0x0024, + 0x22ff: 0x0024, // Block 0x8c, offset 0x2300 - 0x2300: 0x0004, 0x2301: 0x0004, 0x2302: 0x0004, - 0x2327: 0x0004, 0x2328: 0x0004, 0x2329: 0x0004, - 0x232a: 0x0004, 0x232b: 0x0004, 0x232c: 0x0400, 0x232d: 0x0004, 0x232e: 0x0004, 0x232f: 0x0004, - 0x2330: 0x0004, 0x2331: 0x0004, 0x2332: 0x0004, 0x2333: 0x0004, 0x2334: 0x0004, + 0x2300: 0x0024, 0x2301: 0x0024, 0x2302: 0x2000, + 0x2330: 0x2000, 0x2331: 0x2000, 0x2332: 0x2000, 0x2333: 0x0024, 0x2334: 0x0024, 0x2335: 0x0024, + 0x2336: 0x0024, 0x2337: 0x2000, 0x2338: 0x2000, 0x2339: 0x0024, 0x233a: 0x0024, + 0x233d: 0x0800, // Block 0x8d, offset 0x2340 - 0x2345: 0x0400, - 0x2346: 0x0400, - 0x2373: 0x0004, + 0x2342: 0x0024, + 0x234d: 0x0800, // Block 0x8e, offset 0x2380 - 0x2380: 0x0004, 0x2381: 0x0004, 0x2382: 0x0400, - 0x23b3: 0x0400, 0x23b4: 0x0400, 0x23b5: 0x0400, - 0x23b6: 0x0004, 0x23b7: 0x0004, 0x23b8: 0x0004, 0x23b9: 0x0004, 0x23ba: 0x0004, 0x23bb: 0x0004, - 0x23bc: 0x0004, 0x23bd: 0x0004, 0x23be: 0x0004, 0x23bf: 0x0400, + 0x2380: 0x0024, 0x2381: 0x0024, 0x2382: 0x0024, 0x2383: 0x0010, 0x2384: 0x0010, 0x2385: 0x0010, + 0x2386: 0x0010, 0x2387: 0x0010, 0x2388: 0x0010, 0x2389: 0x0010, 0x238a: 0x0010, 0x238b: 0x0010, + 0x238c: 0x0010, 0x238d: 0x0010, 0x238e: 0x0010, 0x238f: 0x0010, 0x2390: 0x0010, 0x2391: 0x0010, + 0x2392: 0x0010, 0x2393: 0x0010, 0x2394: 0x0010, 0x2395: 0x0010, 0x2396: 0x0010, 0x2397: 0x0010, + 0x2398: 0x0010, 0x2399: 0x0010, 0x239a: 0x0010, 0x239b: 0x0010, 0x239c: 0x0010, 0x239d: 0x0010, + 0x239e: 0x0010, 0x239f: 0x0010, 0x23a0: 0x0010, 0x23a1: 0x0010, 0x23a2: 0x0010, 0x23a3: 0x0010, + 0x23a4: 0x0010, 0x23a5: 0x0010, 0x23a6: 0x0010, 0x23a7: 0x0024, 0x23a8: 0x0024, 0x23a9: 0x0024, + 0x23aa: 0x0024, 0x23ab: 0x0024, 0x23ac: 0x2000, 0x23ad: 0x0024, 0x23ae: 0x0024, 0x23af: 0x0024, + 0x23b0: 0x0024, 0x23b1: 0x0024, 0x23b2: 0x0024, 0x23b3: 0x0044, 0x23b4: 0x0024, // Block 0x8f, offset 0x23c0 - 0x23c0: 0x0400, 0x23c2: 0x0100, 0x23c3: 0x0100, - 0x23c9: 0x0004, 0x23ca: 0x0004, 0x23cb: 0x0004, - 0x23cc: 0x0004, 0x23ce: 0x0400, 0x23cf: 0x0004, + 0x23c4: 0x0010, 0x23c5: 0x2000, + 0x23c6: 0x2000, 0x23c7: 0x0010, + 0x23f3: 0x0024, // Block 0x90, offset 0x2400 - 0x242c: 0x0400, 0x242d: 0x0400, 0x242e: 0x0400, 0x242f: 0x0004, - 0x2430: 0x0004, 0x2431: 0x0004, 0x2432: 0x0400, 0x2433: 0x0400, 0x2434: 0x0004, 0x2435: 0x0400, - 0x2436: 0x0004, 0x2437: 0x0004, - 0x243e: 0x0004, + 0x2400: 0x0024, 0x2401: 0x0024, 0x2402: 0x2000, + 0x2433: 0x2000, 0x2434: 0x2000, 0x2435: 0x2000, + 0x2436: 0x0024, 0x2437: 0x0024, 0x2438: 0x0024, 0x2439: 0x0024, 0x243a: 0x0024, 0x243b: 0x0024, + 0x243c: 0x0024, 0x243d: 0x0024, 0x243e: 0x0024, 0x243f: 0x2000, // Block 0x91, offset 0x2440 - 0x2441: 0x0004, + 0x2440: 0x0024, 0x2442: 0x0800, 0x2443: 0x0800, + 0x2449: 0x0024, 0x244a: 0x0024, 0x244b: 0x0024, + 0x244c: 0x0024, 0x244e: 0x2000, 0x244f: 0x0024, // Block 0x92, offset 0x2480 - 0x249f: 0x0004, 0x24a0: 0x0400, 0x24a1: 0x0400, 0x24a2: 0x0400, 0x24a3: 0x0004, - 0x24a4: 0x0004, 0x24a5: 0x0004, 0x24a6: 0x0004, 0x24a7: 0x0004, 0x24a8: 0x0004, 0x24a9: 0x0004, - 0x24aa: 0x0004, + 0x24ac: 0x2000, 0x24ad: 0x2000, 0x24ae: 0x2000, 0x24af: 0x0024, + 0x24b0: 0x0024, 0x24b1: 0x0024, 0x24b2: 0x2000, 0x24b3: 0x2000, 0x24b4: 0x0024, 0x24b5: 0x0024, + 0x24b6: 0x0024, 0x24b7: 0x0024, + 0x24be: 0x0024, // Block 0x93, offset 0x24c0 - 0x24c0: 0x0004, 0x24c1: 0x0400, 0x24c2: 0x0400, 0x24c3: 0x0400, 0x24c4: 0x0400, - 0x24c7: 0x0400, 0x24c8: 0x0400, 0x24cb: 0x0400, - 0x24cc: 0x0400, 0x24cd: 0x0400, - 0x24d7: 0x0004, - 0x24e2: 0x0400, 0x24e3: 0x0400, - 0x24e6: 0x0004, 0x24e7: 0x0004, 0x24e8: 0x0004, 0x24e9: 0x0004, - 0x24ea: 0x0004, 0x24eb: 0x0004, 0x24ec: 0x0004, - 0x24f0: 0x0004, 0x24f1: 0x0004, 0x24f2: 0x0004, 0x24f3: 0x0004, 0x24f4: 0x0004, + 0x24c1: 0x0024, // Block 0x94, offset 0x2500 - 0x2535: 0x0400, - 0x2536: 0x0400, 0x2537: 0x0400, 0x2538: 0x0004, 0x2539: 0x0004, 0x253a: 0x0004, 0x253b: 0x0004, - 0x253c: 0x0004, 0x253d: 0x0004, 0x253e: 0x0004, 0x253f: 0x0004, + 0x251f: 0x0024, 0x2520: 0x2000, 0x2521: 0x2000, 0x2522: 0x2000, 0x2523: 0x0024, + 0x2524: 0x0024, 0x2525: 0x0024, 0x2526: 0x0024, 0x2527: 0x0024, 0x2528: 0x0024, 0x2529: 0x0024, + 0x252a: 0x0024, // Block 0x95, offset 0x2540 - 0x2540: 0x0400, 0x2541: 0x0400, 0x2542: 0x0004, 0x2543: 0x0004, 0x2544: 0x0004, 0x2545: 0x0400, - 0x2546: 0x0004, - 0x255e: 0x0004, + 0x2540: 0x0024, 0x2541: 0x0024, 0x2542: 0x2000, 0x2543: 0x2000, + 0x257b: 0x0024, + 0x257c: 0x0024, 0x257e: 0x0024, 0x257f: 0x2000, // Block 0x96, offset 0x2580 - 0x25b0: 0x0004, 0x25b1: 0x0400, 0x25b2: 0x0400, 0x25b3: 0x0004, 0x25b4: 0x0004, 0x25b5: 0x0004, - 0x25b6: 0x0004, 0x25b7: 0x0004, 0x25b8: 0x0004, 0x25b9: 0x0400, 0x25ba: 0x0004, 0x25bb: 0x0400, - 0x25bc: 0x0400, 0x25bd: 0x0004, 0x25be: 0x0400, 0x25bf: 0x0004, + 0x2580: 0x0024, 0x2581: 0x2000, 0x2582: 0x2000, 0x2583: 0x2000, 0x2584: 0x2000, + 0x2587: 0x2000, 0x2588: 0x2000, 0x258b: 0x2000, + 0x258c: 0x2000, 0x258d: 0x0024, + 0x2597: 0x0024, + 0x25a2: 0x2000, 0x25a3: 0x2000, + 0x25a6: 0x0024, 0x25a7: 0x0024, 0x25a8: 0x0024, 0x25a9: 0x0024, + 0x25aa: 0x0024, 0x25ab: 0x0024, 0x25ac: 0x0024, + 0x25b0: 0x0024, 0x25b1: 0x0024, 0x25b2: 0x0024, 0x25b3: 0x0024, 0x25b4: 0x0024, // Block 0x97, offset 0x25c0 - 0x25c0: 0x0004, 0x25c1: 0x0400, 0x25c2: 0x0004, 0x25c3: 0x0004, + 0x25c0: 0x0010, 0x25c1: 0x0010, 0x25c2: 0x0010, 0x25c3: 0x0010, 0x25c4: 0x0010, 0x25c5: 0x0010, + 0x25c6: 0x0010, 0x25c7: 0x0010, 0x25c8: 0x0010, 0x25c9: 0x0010, 0x25cb: 0x0010, + 0x25ce: 0x0010, 0x25d0: 0x0010, 0x25d1: 0x0010, + 0x25d2: 0x0010, 0x25d3: 0x0010, 0x25d4: 0x0010, 0x25d5: 0x0010, 0x25d6: 0x0010, 0x25d7: 0x0010, + 0x25d8: 0x0010, 0x25d9: 0x0010, 0x25da: 0x0010, 0x25db: 0x0010, 0x25dc: 0x0010, 0x25dd: 0x0010, + 0x25de: 0x0010, 0x25df: 0x0010, 0x25e0: 0x0010, 0x25e1: 0x0010, 0x25e2: 0x0010, 0x25e3: 0x0010, + 0x25e4: 0x0010, 0x25e5: 0x0010, 0x25e6: 0x0010, 0x25e7: 0x0010, 0x25e8: 0x0010, 0x25e9: 0x0010, + 0x25ea: 0x0010, 0x25eb: 0x0010, 0x25ec: 0x0010, 0x25ed: 0x0010, 0x25ee: 0x0010, 0x25ef: 0x0010, + 0x25f0: 0x0010, 0x25f1: 0x0010, 0x25f2: 0x0010, 0x25f3: 0x0010, 0x25f4: 0x0010, 0x25f5: 0x0010, + 0x25f8: 0x0024, 0x25f9: 0x2000, 0x25fa: 0x2000, 0x25fb: 0x0024, + 0x25fc: 0x0024, 0x25fd: 0x0024, 0x25fe: 0x0024, 0x25ff: 0x0024, // Block 0x98, offset 0x2600 - 0x262f: 0x0004, - 0x2630: 0x0400, 0x2631: 0x0400, 0x2632: 0x0004, 0x2633: 0x0004, 0x2634: 0x0004, 0x2635: 0x0004, - 0x2638: 0x0400, 0x2639: 0x0400, 0x263a: 0x0400, 0x263b: 0x0400, - 0x263c: 0x0004, 0x263d: 0x0004, 0x263e: 0x0400, 0x263f: 0x0004, + 0x2600: 0x0024, 0x2602: 0x0024, 0x2605: 0x0024, + 0x2607: 0x0024, 0x2608: 0x0024, 0x2609: 0x0024, 0x260a: 0x2000, + 0x260c: 0x2000, 0x260d: 0x2000, 0x260e: 0x0024, 0x260f: 0x0024, 0x2610: 0x0044, 0x2611: 0x0800, + 0x2612: 0x0024, + 0x2621: 0x0024, 0x2622: 0x0024, // Block 0x99, offset 0x2640 - 0x2640: 0x0004, - 0x265c: 0x0004, 0x265d: 0x0004, + 0x2675: 0x2000, + 0x2676: 0x2000, 0x2677: 0x2000, 0x2678: 0x0024, 0x2679: 0x0024, 0x267a: 0x0024, 0x267b: 0x0024, + 0x267c: 0x0024, 0x267d: 0x0024, 0x267e: 0x0024, 0x267f: 0x0024, // Block 0x9a, offset 0x2680 - 0x26b0: 0x0400, 0x26b1: 0x0400, 0x26b2: 0x0400, 0x26b3: 0x0004, 0x26b4: 0x0004, 0x26b5: 0x0004, - 0x26b6: 0x0004, 0x26b7: 0x0004, 0x26b8: 0x0004, 0x26b9: 0x0004, 0x26ba: 0x0004, 0x26bb: 0x0400, - 0x26bc: 0x0400, 0x26bd: 0x0004, 0x26be: 0x0400, 0x26bf: 0x0004, + 0x2680: 0x2000, 0x2681: 0x2000, 0x2682: 0x0024, 0x2683: 0x0024, 0x2684: 0x0024, 0x2685: 0x2000, + 0x2686: 0x0024, + 0x269e: 0x0024, // Block 0x9b, offset 0x26c0 - 0x26c0: 0x0004, + 0x26f0: 0x0024, 0x26f1: 0x2000, 0x26f2: 0x2000, 0x26f3: 0x0024, 0x26f4: 0x0024, 0x26f5: 0x0024, + 0x26f6: 0x0024, 0x26f7: 0x0024, 0x26f8: 0x0024, 0x26f9: 0x2000, 0x26fa: 0x0024, 0x26fb: 0x2000, + 0x26fc: 0x2000, 0x26fd: 0x0024, 0x26fe: 0x2000, 0x26ff: 0x0024, // Block 0x9c, offset 0x2700 - 0x272b: 0x0004, 0x272c: 0x0400, 0x272d: 0x0004, 0x272e: 0x0400, 0x272f: 0x0400, - 0x2730: 0x0004, 0x2731: 0x0004, 0x2732: 0x0004, 0x2733: 0x0004, 0x2734: 0x0004, 0x2735: 0x0004, - 0x2736: 0x0400, 0x2737: 0x0004, + 0x2700: 0x0024, 0x2701: 0x2000, 0x2702: 0x0024, 0x2703: 0x0024, // Block 0x9d, offset 0x2740 - 0x275d: 0x0004, - 0x275e: 0x0004, 0x275f: 0x0004, 0x2762: 0x0004, 0x2763: 0x0004, - 0x2764: 0x0004, 0x2765: 0x0004, 0x2766: 0x0400, 0x2767: 0x0004, 0x2768: 0x0004, 0x2769: 0x0004, - 0x276a: 0x0004, 0x276b: 0x0004, + 0x276f: 0x0024, + 0x2770: 0x2000, 0x2771: 0x2000, 0x2772: 0x0024, 0x2773: 0x0024, 0x2774: 0x0024, 0x2775: 0x0024, + 0x2778: 0x2000, 0x2779: 0x2000, 0x277a: 0x2000, 0x277b: 0x2000, + 0x277c: 0x0024, 0x277d: 0x0024, 0x277e: 0x2000, 0x277f: 0x0024, // Block 0x9e, offset 0x2780 - 0x27ac: 0x0400, 0x27ad: 0x0400, 0x27ae: 0x0400, 0x27af: 0x0004, - 0x27b0: 0x0004, 0x27b1: 0x0004, 0x27b2: 0x0004, 0x27b3: 0x0004, 0x27b4: 0x0004, 0x27b5: 0x0004, - 0x27b6: 0x0004, 0x27b7: 0x0004, 0x27b8: 0x0400, 0x27b9: 0x0004, 0x27ba: 0x0004, + 0x2780: 0x0024, + 0x279c: 0x0024, 0x279d: 0x0024, // Block 0x9f, offset 0x27c0 - 0x27f0: 0x0004, 0x27f1: 0x0400, 0x27f2: 0x0400, 0x27f3: 0x0400, 0x27f4: 0x0400, 0x27f5: 0x0400, - 0x27f7: 0x0400, 0x27f8: 0x0400, 0x27fb: 0x0004, - 0x27fc: 0x0004, 0x27fd: 0x0400, 0x27fe: 0x0004, 0x27ff: 0x0100, + 0x27f0: 0x2000, 0x27f1: 0x2000, 0x27f2: 0x2000, 0x27f3: 0x0024, 0x27f4: 0x0024, 0x27f5: 0x0024, + 0x27f6: 0x0024, 0x27f7: 0x0024, 0x27f8: 0x0024, 0x27f9: 0x0024, 0x27fa: 0x0024, 0x27fb: 0x2000, + 0x27fc: 0x2000, 0x27fd: 0x0024, 0x27fe: 0x2000, 0x27ff: 0x0024, // Block 0xa0, offset 0x2800 - 0x2800: 0x0400, 0x2801: 0x0100, 0x2802: 0x0400, 0x2803: 0x0004, + 0x2800: 0x0024, // Block 0xa1, offset 0x2840 - 0x2851: 0x0400, - 0x2852: 0x0400, 0x2853: 0x0400, 0x2854: 0x0004, 0x2855: 0x0004, 0x2856: 0x0004, 0x2857: 0x0004, - 0x285a: 0x0004, 0x285b: 0x0004, 0x285c: 0x0400, 0x285d: 0x0400, - 0x285e: 0x0400, 0x285f: 0x0400, 0x2860: 0x0004, - 0x2864: 0x0400, + 0x286b: 0x0024, 0x286c: 0x2000, 0x286d: 0x0024, 0x286e: 0x2000, 0x286f: 0x2000, + 0x2870: 0x0024, 0x2871: 0x0024, 0x2872: 0x0024, 0x2873: 0x0024, 0x2874: 0x0024, 0x2875: 0x0024, + 0x2876: 0x0024, 0x2877: 0x0024, // Block 0xa2, offset 0x2880 - 0x2881: 0x0004, 0x2882: 0x0004, 0x2883: 0x0004, 0x2884: 0x0004, 0x2885: 0x0004, - 0x2886: 0x0004, 0x2887: 0x0004, 0x2888: 0x0004, 0x2889: 0x0004, 0x288a: 0x0004, - 0x28b3: 0x0004, 0x28b4: 0x0004, 0x28b5: 0x0004, - 0x28b6: 0x0004, 0x28b7: 0x0004, 0x28b8: 0x0004, 0x28b9: 0x0400, 0x28ba: 0x0100, 0x28bb: 0x0004, - 0x28bc: 0x0004, 0x28bd: 0x0004, 0x28be: 0x0004, + 0x289d: 0x0024, + 0x289e: 0x2000, 0x289f: 0x0024, 0x28a2: 0x0024, 0x28a3: 0x0024, + 0x28a4: 0x0024, 0x28a5: 0x0024, 0x28a6: 0x2000, 0x28a7: 0x0024, 0x28a8: 0x0024, 0x28a9: 0x0024, + 0x28aa: 0x0024, 0x28ab: 0x0024, // Block 0xa3, offset 0x28c0 - 0x28c7: 0x0004, - 0x28d1: 0x0004, - 0x28d2: 0x0004, 0x28d3: 0x0004, 0x28d4: 0x0004, 0x28d5: 0x0004, 0x28d6: 0x0004, 0x28d7: 0x0400, - 0x28d8: 0x0400, 0x28d9: 0x0004, 0x28da: 0x0004, 0x28db: 0x0004, + 0x28ec: 0x2000, 0x28ed: 0x2000, 0x28ee: 0x2000, 0x28ef: 0x0024, + 0x28f0: 0x0024, 0x28f1: 0x0024, 0x28f2: 0x0024, 0x28f3: 0x0024, 0x28f4: 0x0024, 0x28f5: 0x0024, + 0x28f6: 0x0024, 0x28f7: 0x0024, 0x28f8: 0x2000, 0x28f9: 0x0024, 0x28fa: 0x0024, // Block 0xa4, offset 0x2900 - 0x2904: 0x0100, 0x2905: 0x0100, - 0x2906: 0x0100, 0x2907: 0x0100, 0x2908: 0x0100, 0x2909: 0x0100, 0x290a: 0x0004, 0x290b: 0x0004, - 0x290c: 0x0004, 0x290d: 0x0004, 0x290e: 0x0004, 0x290f: 0x0004, 0x2910: 0x0004, 0x2911: 0x0004, - 0x2912: 0x0004, 0x2913: 0x0004, 0x2914: 0x0004, 0x2915: 0x0004, 0x2916: 0x0004, 0x2917: 0x0400, - 0x2918: 0x0004, 0x2919: 0x0004, + 0x2900: 0x0010, 0x2901: 0x0010, 0x2902: 0x0010, 0x2903: 0x0010, 0x2904: 0x0010, 0x2905: 0x0010, + 0x2906: 0x0010, 0x2909: 0x0010, + 0x290c: 0x0010, 0x290d: 0x0010, 0x290e: 0x0010, 0x290f: 0x0010, 0x2910: 0x0010, 0x2911: 0x0010, + 0x2912: 0x0010, 0x2913: 0x0010, 0x2915: 0x0010, 0x2916: 0x0010, + 0x2918: 0x0010, 0x2919: 0x0010, 0x291a: 0x0010, 0x291b: 0x0010, 0x291c: 0x0010, 0x291d: 0x0010, + 0x291e: 0x0010, 0x291f: 0x0010, 0x2920: 0x0010, 0x2921: 0x0010, 0x2922: 0x0010, 0x2923: 0x0010, + 0x2924: 0x0010, 0x2925: 0x0010, 0x2926: 0x0010, 0x2927: 0x0010, 0x2928: 0x0010, 0x2929: 0x0010, + 0x292a: 0x0010, 0x292b: 0x0010, 0x292c: 0x0010, 0x292d: 0x0010, 0x292e: 0x0010, 0x292f: 0x0010, + 0x2930: 0x0024, 0x2931: 0x2000, 0x2932: 0x2000, 0x2933: 0x2000, 0x2934: 0x2000, 0x2935: 0x2000, + 0x2937: 0x2000, 0x2938: 0x2000, 0x293b: 0x0024, + 0x293c: 0x0024, 0x293d: 0x0024, 0x293e: 0x0044, 0x293f: 0x0800, // Block 0xa5, offset 0x2940 - 0x296f: 0x0400, - 0x2970: 0x0004, 0x2971: 0x0004, 0x2972: 0x0004, 0x2973: 0x0004, 0x2974: 0x0004, 0x2975: 0x0004, - 0x2976: 0x0004, 0x2978: 0x0004, 0x2979: 0x0004, 0x297a: 0x0004, 0x297b: 0x0004, - 0x297c: 0x0004, 0x297d: 0x0004, 0x297e: 0x0400, 0x297f: 0x0004, + 0x2940: 0x2000, 0x2941: 0x0800, 0x2942: 0x2000, 0x2943: 0x0024, // Block 0xa6, offset 0x2980 - 0x2992: 0x0004, 0x2993: 0x0004, 0x2994: 0x0004, 0x2995: 0x0004, 0x2996: 0x0004, 0x2997: 0x0004, - 0x2998: 0x0004, 0x2999: 0x0004, 0x299a: 0x0004, 0x299b: 0x0004, 0x299c: 0x0004, 0x299d: 0x0004, - 0x299e: 0x0004, 0x299f: 0x0004, 0x29a0: 0x0004, 0x29a1: 0x0004, 0x29a2: 0x0004, 0x29a3: 0x0004, - 0x29a4: 0x0004, 0x29a5: 0x0004, 0x29a6: 0x0004, 0x29a7: 0x0004, 0x29a9: 0x0400, - 0x29aa: 0x0004, 0x29ab: 0x0004, 0x29ac: 0x0004, 0x29ad: 0x0004, 0x29ae: 0x0004, 0x29af: 0x0004, - 0x29b0: 0x0004, 0x29b1: 0x0400, 0x29b2: 0x0004, 0x29b3: 0x0004, 0x29b4: 0x0400, 0x29b5: 0x0004, - 0x29b6: 0x0004, + 0x2991: 0x2000, + 0x2992: 0x2000, 0x2993: 0x2000, 0x2994: 0x0024, 0x2995: 0x0024, 0x2996: 0x0024, 0x2997: 0x0024, + 0x299a: 0x0024, 0x299b: 0x0024, 0x299c: 0x2000, 0x299d: 0x2000, + 0x299e: 0x2000, 0x299f: 0x2000, 0x29a0: 0x0024, + 0x29a4: 0x2000, // Block 0xa7, offset 0x29c0 - 0x29f1: 0x0004, 0x29f2: 0x0004, 0x29f3: 0x0004, 0x29f4: 0x0004, 0x29f5: 0x0004, - 0x29f6: 0x0004, 0x29fa: 0x0004, - 0x29fc: 0x0004, 0x29fd: 0x0004, 0x29ff: 0x0004, + 0x29c0: 0x0010, 0x29c1: 0x0024, 0x29c2: 0x0024, 0x29c3: 0x0024, 0x29c4: 0x0024, 0x29c5: 0x0024, + 0x29c6: 0x0024, 0x29c7: 0x0024, 0x29c8: 0x0024, 0x29c9: 0x0024, 0x29ca: 0x0024, 0x29cb: 0x0010, + 0x29cc: 0x0010, 0x29cd: 0x0010, 0x29ce: 0x0010, 0x29cf: 0x0010, 0x29d0: 0x0010, 0x29d1: 0x0010, + 0x29d2: 0x0010, 0x29d3: 0x0010, 0x29d4: 0x0010, 0x29d5: 0x0010, 0x29d6: 0x0010, 0x29d7: 0x0010, + 0x29d8: 0x0010, 0x29d9: 0x0010, 0x29da: 0x0010, 0x29db: 0x0010, 0x29dc: 0x0010, 0x29dd: 0x0010, + 0x29de: 0x0010, 0x29df: 0x0010, 0x29e0: 0x0010, 0x29e1: 0x0010, 0x29e2: 0x0010, 0x29e3: 0x0010, + 0x29e4: 0x0010, 0x29e5: 0x0010, 0x29e6: 0x0010, 0x29e7: 0x0010, 0x29e8: 0x0010, 0x29e9: 0x0010, + 0x29ea: 0x0010, 0x29eb: 0x0010, 0x29ec: 0x0010, 0x29ed: 0x0010, 0x29ee: 0x0010, 0x29ef: 0x0010, + 0x29f0: 0x0010, 0x29f1: 0x0010, 0x29f2: 0x0010, 0x29f3: 0x0024, 0x29f4: 0x0024, 0x29f5: 0x0024, + 0x29f6: 0x0024, 0x29f7: 0x0024, 0x29f8: 0x0024, 0x29f9: 0x2000, 0x29fb: 0x0024, + 0x29fc: 0x0024, 0x29fd: 0x0024, 0x29fe: 0x0024, // Block 0xa8, offset 0x2a00 - 0x2a00: 0x0004, 0x2a01: 0x0004, 0x2a02: 0x0004, 0x2a03: 0x0004, 0x2a04: 0x0004, 0x2a05: 0x0004, - 0x2a06: 0x0100, 0x2a07: 0x0004, + 0x2a07: 0x0044, + 0x2a10: 0x0010, 0x2a11: 0x0024, + 0x2a12: 0x0024, 0x2a13: 0x0024, 0x2a14: 0x0024, 0x2a15: 0x0024, 0x2a16: 0x0024, 0x2a17: 0x2000, + 0x2a18: 0x2000, 0x2a19: 0x0024, 0x2a1a: 0x0024, 0x2a1b: 0x0024, 0x2a1c: 0x0010, 0x2a1d: 0x0010, + 0x2a1e: 0x0010, 0x2a1f: 0x0010, 0x2a20: 0x0010, 0x2a21: 0x0010, 0x2a22: 0x0010, 0x2a23: 0x0010, + 0x2a24: 0x0010, 0x2a25: 0x0010, 0x2a26: 0x0010, 0x2a27: 0x0010, 0x2a28: 0x0010, 0x2a29: 0x0010, + 0x2a2a: 0x0010, 0x2a2b: 0x0010, 0x2a2c: 0x0010, 0x2a2d: 0x0010, 0x2a2e: 0x0010, 0x2a2f: 0x0010, + 0x2a30: 0x0010, 0x2a31: 0x0010, 0x2a32: 0x0010, 0x2a33: 0x0010, 0x2a34: 0x0010, 0x2a35: 0x0010, + 0x2a36: 0x0010, 0x2a37: 0x0010, 0x2a38: 0x0010, 0x2a39: 0x0010, 0x2a3a: 0x0010, 0x2a3b: 0x0010, + 0x2a3c: 0x0010, 0x2a3d: 0x0010, 0x2a3e: 0x0010, 0x2a3f: 0x0010, // Block 0xa9, offset 0x2a40 - 0x2a4a: 0x0400, 0x2a4b: 0x0400, - 0x2a4c: 0x0400, 0x2a4d: 0x0400, 0x2a4e: 0x0400, 0x2a50: 0x0004, 0x2a51: 0x0004, - 0x2a53: 0x0400, 0x2a54: 0x0400, 0x2a55: 0x0004, 0x2a56: 0x0400, 0x2a57: 0x0004, + 0x2a40: 0x0010, 0x2a41: 0x0010, 0x2a42: 0x0010, 0x2a43: 0x0010, 0x2a44: 0x0800, 0x2a45: 0x0800, + 0x2a46: 0x0800, 0x2a47: 0x0800, 0x2a48: 0x0800, 0x2a49: 0x0800, 0x2a4a: 0x0024, 0x2a4b: 0x0024, + 0x2a4c: 0x0024, 0x2a4d: 0x0024, 0x2a4e: 0x0024, 0x2a4f: 0x0024, 0x2a50: 0x0024, 0x2a51: 0x0024, + 0x2a52: 0x0024, 0x2a53: 0x0024, 0x2a54: 0x0024, 0x2a55: 0x0024, 0x2a56: 0x0024, 0x2a57: 0x2000, + 0x2a58: 0x0024, 0x2a59: 0x0044, // Block 0xaa, offset 0x2a80 - 0x2ab3: 0x0004, 0x2ab4: 0x0004, 0x2ab5: 0x0400, - 0x2ab6: 0x0400, + 0x2aa0: 0x0024, 0x2aa1: 0x2000, 0x2aa2: 0x0024, 0x2aa3: 0x0024, + 0x2aa4: 0x0024, 0x2aa5: 0x2000, 0x2aa6: 0x0024, 0x2aa7: 0x2000, // Block 0xab, offset 0x2ac0 - 0x2ac0: 0x0004, 0x2ac1: 0x0004, 0x2ac2: 0x0100, 0x2ac3: 0x0400, - 0x2af4: 0x0400, 0x2af5: 0x0400, - 0x2af6: 0x0004, 0x2af7: 0x0004, 0x2af8: 0x0004, 0x2af9: 0x0004, 0x2afa: 0x0004, - 0x2afe: 0x0400, 0x2aff: 0x0400, + 0x2aef: 0x2000, + 0x2af0: 0x0024, 0x2af1: 0x0024, 0x2af2: 0x0024, 0x2af3: 0x0024, 0x2af4: 0x0024, 0x2af5: 0x0024, + 0x2af6: 0x0024, 0x2af8: 0x0024, 0x2af9: 0x0024, 0x2afa: 0x0024, 0x2afb: 0x0024, + 0x2afc: 0x0024, 0x2afd: 0x0024, 0x2afe: 0x2000, 0x2aff: 0x0024, // Block 0xac, offset 0x2b00 - 0x2b00: 0x0004, 0x2b01: 0x0400, 0x2b02: 0x0004, + 0x2b12: 0x0024, 0x2b13: 0x0024, 0x2b14: 0x0024, 0x2b15: 0x0024, 0x2b16: 0x0024, 0x2b17: 0x0024, + 0x2b18: 0x0024, 0x2b19: 0x0024, 0x2b1a: 0x0024, 0x2b1b: 0x0024, 0x2b1c: 0x0024, 0x2b1d: 0x0024, + 0x2b1e: 0x0024, 0x2b1f: 0x0024, 0x2b20: 0x0024, 0x2b21: 0x0024, 0x2b22: 0x0024, 0x2b23: 0x0024, + 0x2b24: 0x0024, 0x2b25: 0x0024, 0x2b26: 0x0024, 0x2b27: 0x0024, 0x2b29: 0x2000, + 0x2b2a: 0x0024, 0x2b2b: 0x0024, 0x2b2c: 0x0024, 0x2b2d: 0x0024, 0x2b2e: 0x0024, 0x2b2f: 0x0024, + 0x2b30: 0x0024, 0x2b31: 0x2000, 0x2b32: 0x0024, 0x2b33: 0x0024, 0x2b34: 0x2000, 0x2b35: 0x0024, + 0x2b36: 0x0024, // Block 0xad, offset 0x2b40 - 0x2b70: 0x0002, 0x2b71: 0x0002, 0x2b72: 0x0002, 0x2b73: 0x0002, 0x2b74: 0x0002, 0x2b75: 0x0002, - 0x2b76: 0x0002, 0x2b77: 0x0002, 0x2b78: 0x0002, 0x2b79: 0x0002, 0x2b7a: 0x0002, 0x2b7b: 0x0002, - 0x2b7c: 0x0002, 0x2b7d: 0x0002, 0x2b7e: 0x0002, 0x2b7f: 0x0002, + 0x2b71: 0x0024, 0x2b72: 0x0024, 0x2b73: 0x0024, 0x2b74: 0x0024, 0x2b75: 0x0024, + 0x2b76: 0x0024, 0x2b7a: 0x0024, + 0x2b7c: 0x0024, 0x2b7d: 0x0024, 0x2b7f: 0x0024, // Block 0xae, offset 0x2b80 - 0x2b80: 0x0004, - 0x2b87: 0x0004, 0x2b88: 0x0004, 0x2b89: 0x0004, 0x2b8a: 0x0004, 0x2b8b: 0x0004, - 0x2b8c: 0x0004, 0x2b8d: 0x0004, 0x2b8e: 0x0004, 0x2b8f: 0x0004, 0x2b90: 0x0004, 0x2b91: 0x0004, - 0x2b92: 0x0004, 0x2b93: 0x0004, 0x2b94: 0x0004, 0x2b95: 0x0004, + 0x2b80: 0x0024, 0x2b81: 0x0024, 0x2b82: 0x0024, 0x2b83: 0x0024, 0x2b84: 0x0024, 0x2b85: 0x0024, + 0x2b86: 0x0800, 0x2b87: 0x0024, // Block 0xaf, offset 0x2bc0 - 0x2bf0: 0x0004, 0x2bf1: 0x0004, 0x2bf2: 0x0004, 0x2bf3: 0x0004, 0x2bf4: 0x0004, + 0x2bca: 0x2000, 0x2bcb: 0x2000, + 0x2bcc: 0x2000, 0x2bcd: 0x2000, 0x2bce: 0x2000, 0x2bd0: 0x0024, 0x2bd1: 0x0024, + 0x2bd3: 0x2000, 0x2bd4: 0x2000, 0x2bd5: 0x0024, 0x2bd6: 0x2000, 0x2bd7: 0x0024, // Block 0xb0, offset 0x2c00 - 0x2c30: 0x0004, 0x2c31: 0x0004, 0x2c32: 0x0004, 0x2c33: 0x0004, 0x2c34: 0x0004, 0x2c35: 0x0004, - 0x2c36: 0x0004, + 0x2c33: 0x0024, 0x2c34: 0x0024, 0x2c35: 0x2000, + 0x2c36: 0x2000, // Block 0xb1, offset 0x2c40 - 0x2c4f: 0x0004, 0x2c51: 0x0400, - 0x2c52: 0x0400, 0x2c53: 0x0400, 0x2c54: 0x0400, 0x2c55: 0x0400, 0x2c56: 0x0400, 0x2c57: 0x0400, - 0x2c58: 0x0400, 0x2c59: 0x0400, 0x2c5a: 0x0400, 0x2c5b: 0x0400, 0x2c5c: 0x0400, 0x2c5d: 0x0400, - 0x2c5e: 0x0400, 0x2c5f: 0x0400, 0x2c60: 0x0400, 0x2c61: 0x0400, 0x2c62: 0x0400, 0x2c63: 0x0400, - 0x2c64: 0x0400, 0x2c65: 0x0400, 0x2c66: 0x0400, 0x2c67: 0x0400, 0x2c68: 0x0400, 0x2c69: 0x0400, - 0x2c6a: 0x0400, 0x2c6b: 0x0400, 0x2c6c: 0x0400, 0x2c6d: 0x0400, 0x2c6e: 0x0400, 0x2c6f: 0x0400, - 0x2c70: 0x0400, 0x2c71: 0x0400, 0x2c72: 0x0400, 0x2c73: 0x0400, 0x2c74: 0x0400, 0x2c75: 0x0400, - 0x2c76: 0x0400, 0x2c77: 0x0400, 0x2c78: 0x0400, 0x2c79: 0x0400, 0x2c7a: 0x0400, 0x2c7b: 0x0400, - 0x2c7c: 0x0400, 0x2c7d: 0x0400, 0x2c7e: 0x0400, 0x2c7f: 0x0400, + 0x2c40: 0x0024, 0x2c41: 0x0024, 0x2c42: 0x0800, 0x2c43: 0x2000, 0x2c44: 0x0010, 0x2c45: 0x0010, + 0x2c46: 0x0010, 0x2c47: 0x0010, 0x2c48: 0x0010, 0x2c49: 0x0010, 0x2c4a: 0x0010, 0x2c4b: 0x0010, + 0x2c4c: 0x0010, 0x2c4d: 0x0010, 0x2c4e: 0x0010, 0x2c4f: 0x0010, 0x2c50: 0x0010, + 0x2c52: 0x0010, 0x2c53: 0x0010, 0x2c54: 0x0010, 0x2c55: 0x0010, 0x2c56: 0x0010, 0x2c57: 0x0010, + 0x2c58: 0x0010, 0x2c59: 0x0010, 0x2c5a: 0x0010, 0x2c5b: 0x0010, 0x2c5c: 0x0010, 0x2c5d: 0x0010, + 0x2c5e: 0x0010, 0x2c5f: 0x0010, 0x2c60: 0x0010, 0x2c61: 0x0010, 0x2c62: 0x0010, 0x2c63: 0x0010, + 0x2c64: 0x0010, 0x2c65: 0x0010, 0x2c66: 0x0010, 0x2c67: 0x0010, 0x2c68: 0x0010, 0x2c69: 0x0010, + 0x2c6a: 0x0010, 0x2c6b: 0x0010, 0x2c6c: 0x0010, 0x2c6d: 0x0010, 0x2c6e: 0x0010, 0x2c6f: 0x0010, + 0x2c70: 0x0010, 0x2c71: 0x0010, 0x2c72: 0x0010, 0x2c73: 0x0010, 0x2c74: 0x2000, 0x2c75: 0x2000, + 0x2c76: 0x0024, 0x2c77: 0x0024, 0x2c78: 0x0024, 0x2c79: 0x0024, 0x2c7a: 0x0024, + 0x2c7e: 0x2000, 0x2c7f: 0x2000, // Block 0xb2, offset 0x2c80 - 0x2c80: 0x0400, 0x2c81: 0x0400, 0x2c82: 0x0400, 0x2c83: 0x0400, 0x2c84: 0x0400, 0x2c85: 0x0400, - 0x2c86: 0x0400, 0x2c87: 0x0400, - 0x2c8f: 0x0004, 0x2c90: 0x0004, 0x2c91: 0x0004, - 0x2c92: 0x0004, + 0x2c80: 0x0024, 0x2c81: 0x0024, 0x2c82: 0x0044, + 0x2c9a: 0x0024, // Block 0xb3, offset 0x2cc0 - 0x2ce4: 0x0004, - 0x2cf0: 0x0400, 0x2cf1: 0x0400, + 0x2cf0: 0x0002, 0x2cf1: 0x0002, 0x2cf2: 0x0002, 0x2cf3: 0x0002, 0x2cf4: 0x0002, 0x2cf5: 0x0002, + 0x2cf6: 0x0002, 0x2cf7: 0x0002, 0x2cf8: 0x0002, 0x2cf9: 0x0002, 0x2cfa: 0x0002, 0x2cfb: 0x0002, + 0x2cfc: 0x0002, 0x2cfd: 0x0002, 0x2cfe: 0x0002, 0x2cff: 0x0002, // Block 0xb4, offset 0x2d00 - 0x2d1d: 0x0004, - 0x2d1e: 0x0004, 0x2d20: 0x0002, 0x2d21: 0x0002, 0x2d22: 0x0002, 0x2d23: 0x0002, + 0x2d00: 0x0024, + 0x2d07: 0x0024, 0x2d08: 0x0024, 0x2d09: 0x0024, 0x2d0a: 0x0024, 0x2d0b: 0x0024, + 0x2d0c: 0x0024, 0x2d0d: 0x0024, 0x2d0e: 0x0024, 0x2d0f: 0x0024, 0x2d10: 0x0024, 0x2d11: 0x0024, + 0x2d12: 0x0024, 0x2d13: 0x0024, 0x2d14: 0x0024, 0x2d15: 0x0024, // Block 0xb5, offset 0x2d40 - 0x2d40: 0x0004, 0x2d41: 0x0004, 0x2d42: 0x0004, 0x2d43: 0x0004, 0x2d44: 0x0004, 0x2d45: 0x0004, - 0x2d46: 0x0004, 0x2d47: 0x0004, 0x2d48: 0x0004, 0x2d49: 0x0004, 0x2d4a: 0x0004, 0x2d4b: 0x0004, - 0x2d4c: 0x0004, 0x2d4d: 0x0004, 0x2d4e: 0x0004, 0x2d4f: 0x0004, 0x2d50: 0x0004, 0x2d51: 0x0004, - 0x2d52: 0x0004, 0x2d53: 0x0004, 0x2d54: 0x0004, 0x2d55: 0x0004, 0x2d56: 0x0004, 0x2d57: 0x0004, - 0x2d58: 0x0004, 0x2d59: 0x0004, 0x2d5a: 0x0004, 0x2d5b: 0x0004, 0x2d5c: 0x0004, 0x2d5d: 0x0004, - 0x2d5e: 0x0004, 0x2d5f: 0x0004, 0x2d60: 0x0004, 0x2d61: 0x0004, 0x2d62: 0x0004, 0x2d63: 0x0004, - 0x2d64: 0x0004, 0x2d65: 0x0004, 0x2d66: 0x0004, 0x2d67: 0x0004, 0x2d68: 0x0004, 0x2d69: 0x0004, - 0x2d6a: 0x0004, 0x2d6b: 0x0004, 0x2d6c: 0x0004, 0x2d6d: 0x0004, - 0x2d70: 0x0004, 0x2d71: 0x0004, 0x2d72: 0x0004, 0x2d73: 0x0004, 0x2d74: 0x0004, 0x2d75: 0x0004, - 0x2d76: 0x0004, 0x2d77: 0x0004, 0x2d78: 0x0004, 0x2d79: 0x0004, 0x2d7a: 0x0004, 0x2d7b: 0x0004, - 0x2d7c: 0x0004, 0x2d7d: 0x0004, 0x2d7e: 0x0004, 0x2d7f: 0x0004, + 0x2d5e: 0x0024, 0x2d5f: 0x0024, 0x2d60: 0x0024, 0x2d61: 0x0024, 0x2d62: 0x0024, 0x2d63: 0x0024, + 0x2d64: 0x0024, 0x2d65: 0x0024, 0x2d66: 0x0024, 0x2d67: 0x0024, 0x2d68: 0x0024, 0x2d69: 0x0024, + 0x2d6a: 0x2000, 0x2d6b: 0x2000, 0x2d6c: 0x2000, 0x2d6d: 0x0024, 0x2d6e: 0x0024, 0x2d6f: 0x0024, // Block 0xb6, offset 0x2d80 - 0x2d80: 0x0004, 0x2d81: 0x0004, 0x2d82: 0x0004, 0x2d83: 0x0004, 0x2d84: 0x0004, 0x2d85: 0x0004, - 0x2d86: 0x0004, + 0x2db0: 0x0024, 0x2db1: 0x0024, 0x2db2: 0x0024, 0x2db3: 0x0024, 0x2db4: 0x0024, // Block 0xb7, offset 0x2dc0 - 0x2de5: 0x0004, 0x2de6: 0x0400, 0x2de7: 0x0004, 0x2de8: 0x0004, 0x2de9: 0x0004, - 0x2ded: 0x0400, 0x2dee: 0x0004, 0x2def: 0x0004, - 0x2df0: 0x0004, 0x2df1: 0x0004, 0x2df2: 0x0004, 0x2df3: 0x0002, 0x2df4: 0x0002, 0x2df5: 0x0002, - 0x2df6: 0x0002, 0x2df7: 0x0002, 0x2df8: 0x0002, 0x2df9: 0x0002, 0x2dfa: 0x0002, 0x2dfb: 0x0004, - 0x2dfc: 0x0004, 0x2dfd: 0x0004, 0x2dfe: 0x0004, 0x2dff: 0x0004, + 0x2df0: 0x0024, 0x2df1: 0x0024, 0x2df2: 0x0024, 0x2df3: 0x0024, 0x2df4: 0x0024, 0x2df5: 0x0024, + 0x2df6: 0x0024, // Block 0xb8, offset 0x2e00 - 0x2e00: 0x0004, 0x2e01: 0x0004, 0x2e02: 0x0004, 0x2e05: 0x0004, - 0x2e06: 0x0004, 0x2e07: 0x0004, 0x2e08: 0x0004, 0x2e09: 0x0004, 0x2e0a: 0x0004, 0x2e0b: 0x0004, - 0x2e2a: 0x0004, 0x2e2b: 0x0004, 0x2e2c: 0x0004, 0x2e2d: 0x0004, + 0x2e23: 0x8000, + 0x2e27: 0x8000, 0x2e28: 0x8000, 0x2e29: 0x8000, + 0x2e2a: 0x8000, // Block 0xb9, offset 0x2e40 - 0x2e42: 0x0004, 0x2e43: 0x0004, 0x2e44: 0x0004, + 0x2e4f: 0x0024, 0x2e51: 0x2000, + 0x2e52: 0x2000, 0x2e53: 0x2000, 0x2e54: 0x2000, 0x2e55: 0x2000, 0x2e56: 0x2000, 0x2e57: 0x2000, + 0x2e58: 0x2000, 0x2e59: 0x2000, 0x2e5a: 0x2000, 0x2e5b: 0x2000, 0x2e5c: 0x2000, 0x2e5d: 0x2000, + 0x2e5e: 0x2000, 0x2e5f: 0x2000, 0x2e60: 0x2000, 0x2e61: 0x2000, 0x2e62: 0x2000, 0x2e63: 0x2000, + 0x2e64: 0x2000, 0x2e65: 0x2000, 0x2e66: 0x2000, 0x2e67: 0x2000, 0x2e68: 0x2000, 0x2e69: 0x2000, + 0x2e6a: 0x2000, 0x2e6b: 0x2000, 0x2e6c: 0x2000, 0x2e6d: 0x2000, 0x2e6e: 0x2000, 0x2e6f: 0x2000, + 0x2e70: 0x2000, 0x2e71: 0x2000, 0x2e72: 0x2000, 0x2e73: 0x2000, 0x2e74: 0x2000, 0x2e75: 0x2000, + 0x2e76: 0x2000, 0x2e77: 0x2000, 0x2e78: 0x2000, 0x2e79: 0x2000, 0x2e7a: 0x2000, 0x2e7b: 0x2000, + 0x2e7c: 0x2000, 0x2e7d: 0x2000, 0x2e7e: 0x2000, 0x2e7f: 0x2000, // Block 0xba, offset 0x2e80 - 0x2e80: 0x0004, 0x2e81: 0x0004, 0x2e82: 0x0004, 0x2e83: 0x0004, 0x2e84: 0x0004, 0x2e85: 0x0004, - 0x2e86: 0x0004, 0x2e87: 0x0004, 0x2e88: 0x0004, 0x2e89: 0x0004, 0x2e8a: 0x0004, 0x2e8b: 0x0004, - 0x2e8c: 0x0004, 0x2e8d: 0x0004, 0x2e8e: 0x0004, 0x2e8f: 0x0004, 0x2e90: 0x0004, 0x2e91: 0x0004, - 0x2e92: 0x0004, 0x2e93: 0x0004, 0x2e94: 0x0004, 0x2e95: 0x0004, 0x2e96: 0x0004, 0x2e97: 0x0004, - 0x2e98: 0x0004, 0x2e99: 0x0004, 0x2e9a: 0x0004, 0x2e9b: 0x0004, 0x2e9c: 0x0004, 0x2e9d: 0x0004, - 0x2e9e: 0x0004, 0x2e9f: 0x0004, 0x2ea0: 0x0004, 0x2ea1: 0x0004, 0x2ea2: 0x0004, 0x2ea3: 0x0004, - 0x2ea4: 0x0004, 0x2ea5: 0x0004, 0x2ea6: 0x0004, 0x2ea7: 0x0004, 0x2ea8: 0x0004, 0x2ea9: 0x0004, - 0x2eaa: 0x0004, 0x2eab: 0x0004, 0x2eac: 0x0004, 0x2ead: 0x0004, 0x2eae: 0x0004, 0x2eaf: 0x0004, - 0x2eb0: 0x0004, 0x2eb1: 0x0004, 0x2eb2: 0x0004, 0x2eb3: 0x0004, 0x2eb4: 0x0004, 0x2eb5: 0x0004, - 0x2eb6: 0x0004, 0x2ebb: 0x0004, - 0x2ebc: 0x0004, 0x2ebd: 0x0004, 0x2ebe: 0x0004, 0x2ebf: 0x0004, + 0x2e80: 0x2000, 0x2e81: 0x2000, 0x2e82: 0x2000, 0x2e83: 0x2000, 0x2e84: 0x2000, 0x2e85: 0x2000, + 0x2e86: 0x2000, 0x2e87: 0x2000, + 0x2e8f: 0x0024, 0x2e90: 0x0024, 0x2e91: 0x0024, + 0x2e92: 0x0024, // Block 0xbb, offset 0x2ec0 - 0x2ec0: 0x0004, 0x2ec1: 0x0004, 0x2ec2: 0x0004, 0x2ec3: 0x0004, 0x2ec4: 0x0004, 0x2ec5: 0x0004, - 0x2ec6: 0x0004, 0x2ec7: 0x0004, 0x2ec8: 0x0004, 0x2ec9: 0x0004, 0x2eca: 0x0004, 0x2ecb: 0x0004, - 0x2ecc: 0x0004, 0x2ecd: 0x0004, 0x2ece: 0x0004, 0x2ecf: 0x0004, 0x2ed0: 0x0004, 0x2ed1: 0x0004, - 0x2ed2: 0x0004, 0x2ed3: 0x0004, 0x2ed4: 0x0004, 0x2ed5: 0x0004, 0x2ed6: 0x0004, 0x2ed7: 0x0004, - 0x2ed8: 0x0004, 0x2ed9: 0x0004, 0x2eda: 0x0004, 0x2edb: 0x0004, 0x2edc: 0x0004, 0x2edd: 0x0004, - 0x2ede: 0x0004, 0x2edf: 0x0004, 0x2ee0: 0x0004, 0x2ee1: 0x0004, 0x2ee2: 0x0004, 0x2ee3: 0x0004, - 0x2ee4: 0x0004, 0x2ee5: 0x0004, 0x2ee6: 0x0004, 0x2ee7: 0x0004, 0x2ee8: 0x0004, 0x2ee9: 0x0004, - 0x2eea: 0x0004, 0x2eeb: 0x0004, 0x2eec: 0x0004, - 0x2ef5: 0x0004, + 0x2ee4: 0x0024, + 0x2ef0: 0x0024, 0x2ef1: 0x0024, // Block 0xbc, offset 0x2f00 - 0x2f04: 0x0004, - 0x2f1b: 0x0004, 0x2f1c: 0x0004, 0x2f1d: 0x0004, - 0x2f1e: 0x0004, 0x2f1f: 0x0004, 0x2f21: 0x0004, 0x2f22: 0x0004, 0x2f23: 0x0004, - 0x2f24: 0x0004, 0x2f25: 0x0004, 0x2f26: 0x0004, 0x2f27: 0x0004, 0x2f28: 0x0004, 0x2f29: 0x0004, - 0x2f2a: 0x0004, 0x2f2b: 0x0004, 0x2f2c: 0x0004, 0x2f2d: 0x0004, 0x2f2e: 0x0004, 0x2f2f: 0x0004, + 0x2f1d: 0x0024, + 0x2f1e: 0x0024, 0x2f20: 0x0002, 0x2f21: 0x0002, 0x2f22: 0x0002, 0x2f23: 0x0002, // Block 0xbd, offset 0x2f40 - 0x2f40: 0x0004, 0x2f41: 0x0004, 0x2f42: 0x0004, 0x2f43: 0x0004, 0x2f44: 0x0004, 0x2f45: 0x0004, - 0x2f46: 0x0004, 0x2f48: 0x0004, 0x2f49: 0x0004, 0x2f4a: 0x0004, 0x2f4b: 0x0004, - 0x2f4c: 0x0004, 0x2f4d: 0x0004, 0x2f4e: 0x0004, 0x2f4f: 0x0004, 0x2f50: 0x0004, 0x2f51: 0x0004, - 0x2f52: 0x0004, 0x2f53: 0x0004, 0x2f54: 0x0004, 0x2f55: 0x0004, 0x2f56: 0x0004, 0x2f57: 0x0004, - 0x2f58: 0x0004, 0x2f5b: 0x0004, 0x2f5c: 0x0004, 0x2f5d: 0x0004, - 0x2f5e: 0x0004, 0x2f5f: 0x0004, 0x2f60: 0x0004, 0x2f61: 0x0004, 0x2f63: 0x0004, - 0x2f64: 0x0004, 0x2f66: 0x0004, 0x2f67: 0x0004, 0x2f68: 0x0004, 0x2f69: 0x0004, - 0x2f6a: 0x0004, + 0x2f40: 0x0024, 0x2f41: 0x0024, 0x2f42: 0x0024, 0x2f43: 0x0024, 0x2f44: 0x0024, 0x2f45: 0x0024, + 0x2f46: 0x0024, 0x2f47: 0x0024, 0x2f48: 0x0024, 0x2f49: 0x0024, 0x2f4a: 0x0024, 0x2f4b: 0x0024, + 0x2f4c: 0x0024, 0x2f4d: 0x0024, 0x2f4e: 0x0024, 0x2f4f: 0x0024, 0x2f50: 0x0024, 0x2f51: 0x0024, + 0x2f52: 0x0024, 0x2f53: 0x0024, 0x2f54: 0x0024, 0x2f55: 0x0024, 0x2f56: 0x0024, 0x2f57: 0x0024, + 0x2f58: 0x0024, 0x2f59: 0x0024, 0x2f5a: 0x0024, 0x2f5b: 0x0024, 0x2f5c: 0x0024, 0x2f5d: 0x0024, + 0x2f5e: 0x0024, 0x2f5f: 0x0024, 0x2f60: 0x0024, 0x2f61: 0x0024, 0x2f62: 0x0024, 0x2f63: 0x0024, + 0x2f64: 0x0024, 0x2f65: 0x0024, 0x2f66: 0x0024, 0x2f67: 0x0024, 0x2f68: 0x0024, 0x2f69: 0x0024, + 0x2f6a: 0x0024, 0x2f6b: 0x0024, 0x2f6c: 0x0024, 0x2f6d: 0x0024, + 0x2f70: 0x0024, 0x2f71: 0x0024, 0x2f72: 0x0024, 0x2f73: 0x0024, 0x2f74: 0x0024, 0x2f75: 0x0024, + 0x2f76: 0x0024, 0x2f77: 0x0024, 0x2f78: 0x0024, 0x2f79: 0x0024, 0x2f7a: 0x0024, 0x2f7b: 0x0024, + 0x2f7c: 0x0024, 0x2f7d: 0x0024, 0x2f7e: 0x0024, 0x2f7f: 0x0024, // Block 0xbe, offset 0x2f80 - 0x2f8f: 0x0004, + 0x2f80: 0x0024, 0x2f81: 0x0024, 0x2f82: 0x0024, 0x2f83: 0x0024, 0x2f84: 0x0024, 0x2f85: 0x0024, + 0x2f86: 0x0024, // Block 0xbf, offset 0x2fc0 - 0x2fee: 0x0004, + 0x2fe5: 0x0024, 0x2fe6: 0x0024, 0x2fe7: 0x0024, 0x2fe8: 0x0024, 0x2fe9: 0x0024, + 0x2fed: 0x0024, 0x2fee: 0x0024, 0x2fef: 0x0024, + 0x2ff0: 0x0024, 0x2ff1: 0x0024, 0x2ff2: 0x0024, 0x2ff3: 0x0002, 0x2ff4: 0x0002, 0x2ff5: 0x0002, + 0x2ff6: 0x0002, 0x2ff7: 0x0002, 0x2ff8: 0x0002, 0x2ff9: 0x0002, 0x2ffa: 0x0002, 0x2ffb: 0x0024, + 0x2ffc: 0x0024, 0x2ffd: 0x0024, 0x2ffe: 0x0024, 0x2fff: 0x0024, // Block 0xc0, offset 0x3000 - 0x302c: 0x0004, 0x302d: 0x0004, 0x302e: 0x0004, 0x302f: 0x0004, + 0x3000: 0x0024, 0x3001: 0x0024, 0x3002: 0x0024, 0x3005: 0x0024, + 0x3006: 0x0024, 0x3007: 0x0024, 0x3008: 0x0024, 0x3009: 0x0024, 0x300a: 0x0024, 0x300b: 0x0024, + 0x302a: 0x0024, 0x302b: 0x0024, 0x302c: 0x0024, 0x302d: 0x0024, // Block 0xc1, offset 0x3040 - 0x3050: 0x0004, 0x3051: 0x0004, - 0x3052: 0x0004, 0x3053: 0x0004, 0x3054: 0x0004, 0x3055: 0x0004, 0x3056: 0x0004, + 0x3042: 0x0024, 0x3043: 0x0024, 0x3044: 0x0024, // Block 0xc2, offset 0x3080 - 0x3084: 0x0004, 0x3085: 0x0004, - 0x3086: 0x0004, 0x3087: 0x0004, 0x3088: 0x0004, 0x3089: 0x0004, 0x308a: 0x0004, + 0x3080: 0x0024, 0x3081: 0x0024, 0x3082: 0x0024, 0x3083: 0x0024, 0x3084: 0x0024, 0x3085: 0x0024, + 0x3086: 0x0024, 0x3087: 0x0024, 0x3088: 0x0024, 0x3089: 0x0024, 0x308a: 0x0024, 0x308b: 0x0024, + 0x308c: 0x0024, 0x308d: 0x0024, 0x308e: 0x0024, 0x308f: 0x0024, 0x3090: 0x0024, 0x3091: 0x0024, + 0x3092: 0x0024, 0x3093: 0x0024, 0x3094: 0x0024, 0x3095: 0x0024, 0x3096: 0x0024, 0x3097: 0x0024, + 0x3098: 0x0024, 0x3099: 0x0024, 0x309a: 0x0024, 0x309b: 0x0024, 0x309c: 0x0024, 0x309d: 0x0024, + 0x309e: 0x0024, 0x309f: 0x0024, 0x30a0: 0x0024, 0x30a1: 0x0024, 0x30a2: 0x0024, 0x30a3: 0x0024, + 0x30a4: 0x0024, 0x30a5: 0x0024, 0x30a6: 0x0024, 0x30a7: 0x0024, 0x30a8: 0x0024, 0x30a9: 0x0024, + 0x30aa: 0x0024, 0x30ab: 0x0024, 0x30ac: 0x0024, 0x30ad: 0x0024, 0x30ae: 0x0024, 0x30af: 0x0024, + 0x30b0: 0x0024, 0x30b1: 0x0024, 0x30b2: 0x0024, 0x30b3: 0x0024, 0x30b4: 0x0024, 0x30b5: 0x0024, + 0x30b6: 0x0024, 0x30bb: 0x0024, + 0x30bc: 0x0024, 0x30bd: 0x0024, 0x30be: 0x0024, 0x30bf: 0x0024, // Block 0xc3, offset 0x30c0 - 0x30cd: 0x0008, 0x30ce: 0x0008, 0x30cf: 0x0008, - 0x30ef: 0x0008, + 0x30c0: 0x0024, 0x30c1: 0x0024, 0x30c2: 0x0024, 0x30c3: 0x0024, 0x30c4: 0x0024, 0x30c5: 0x0024, + 0x30c6: 0x0024, 0x30c7: 0x0024, 0x30c8: 0x0024, 0x30c9: 0x0024, 0x30ca: 0x0024, 0x30cb: 0x0024, + 0x30cc: 0x0024, 0x30cd: 0x0024, 0x30ce: 0x0024, 0x30cf: 0x0024, 0x30d0: 0x0024, 0x30d1: 0x0024, + 0x30d2: 0x0024, 0x30d3: 0x0024, 0x30d4: 0x0024, 0x30d5: 0x0024, 0x30d6: 0x0024, 0x30d7: 0x0024, + 0x30d8: 0x0024, 0x30d9: 0x0024, 0x30da: 0x0024, 0x30db: 0x0024, 0x30dc: 0x0024, 0x30dd: 0x0024, + 0x30de: 0x0024, 0x30df: 0x0024, 0x30e0: 0x0024, 0x30e1: 0x0024, 0x30e2: 0x0024, 0x30e3: 0x0024, + 0x30e4: 0x0024, 0x30e5: 0x0024, 0x30e6: 0x0024, 0x30e7: 0x0024, 0x30e8: 0x0024, 0x30e9: 0x0024, + 0x30ea: 0x0024, 0x30eb: 0x0024, 0x30ec: 0x0024, + 0x30f5: 0x0024, // Block 0xc4, offset 0x3100 - 0x312c: 0x0008, 0x312d: 0x0008, 0x312e: 0x0008, 0x312f: 0x0008, - 0x3130: 0x0008, 0x3131: 0x0008, - 0x313e: 0x0008, 0x313f: 0x0008, + 0x3104: 0x0024, + 0x311b: 0x0024, 0x311c: 0x0024, 0x311d: 0x0024, + 0x311e: 0x0024, 0x311f: 0x0024, 0x3121: 0x0024, 0x3122: 0x0024, 0x3123: 0x0024, + 0x3124: 0x0024, 0x3125: 0x0024, 0x3126: 0x0024, 0x3127: 0x0024, 0x3128: 0x0024, 0x3129: 0x0024, + 0x312a: 0x0024, 0x312b: 0x0024, 0x312c: 0x0024, 0x312d: 0x0024, 0x312e: 0x0024, 0x312f: 0x0024, // Block 0xc5, offset 0x3140 - 0x314e: 0x0008, 0x3151: 0x0008, - 0x3152: 0x0008, 0x3153: 0x0008, 0x3154: 0x0008, 0x3155: 0x0008, 0x3156: 0x0008, 0x3157: 0x0008, - 0x3158: 0x0008, 0x3159: 0x0008, 0x315a: 0x0008, - 0x316d: 0x0008, 0x316e: 0x0008, 0x316f: 0x0008, - 0x3170: 0x0008, 0x3171: 0x0008, 0x3172: 0x0008, 0x3173: 0x0008, 0x3174: 0x0008, 0x3175: 0x0008, - 0x3176: 0x0008, 0x3177: 0x0008, 0x3178: 0x0008, 0x3179: 0x0008, 0x317a: 0x0008, 0x317b: 0x0008, - 0x317c: 0x0008, 0x317d: 0x0008, 0x317e: 0x0008, 0x317f: 0x0008, + 0x3140: 0x0024, 0x3141: 0x0024, 0x3142: 0x0024, 0x3143: 0x0024, 0x3144: 0x0024, 0x3145: 0x0024, + 0x3146: 0x0024, 0x3148: 0x0024, 0x3149: 0x0024, 0x314a: 0x0024, 0x314b: 0x0024, + 0x314c: 0x0024, 0x314d: 0x0024, 0x314e: 0x0024, 0x314f: 0x0024, 0x3150: 0x0024, 0x3151: 0x0024, + 0x3152: 0x0024, 0x3153: 0x0024, 0x3154: 0x0024, 0x3155: 0x0024, 0x3156: 0x0024, 0x3157: 0x0024, + 0x3158: 0x0024, 0x315b: 0x0024, 0x315c: 0x0024, 0x315d: 0x0024, + 0x315e: 0x0024, 0x315f: 0x0024, 0x3160: 0x0024, 0x3161: 0x0024, 0x3163: 0x0024, + 0x3164: 0x0024, 0x3166: 0x0024, 0x3167: 0x0024, 0x3168: 0x0024, 0x3169: 0x0024, + 0x316a: 0x0024, // Block 0xc6, offset 0x3180 - 0x3180: 0x0008, 0x3181: 0x0008, 0x3182: 0x0008, 0x3183: 0x0008, 0x3184: 0x0008, 0x3185: 0x0008, - 0x3186: 0x0008, 0x3187: 0x0008, 0x3188: 0x0008, 0x3189: 0x0008, 0x318a: 0x0008, 0x318b: 0x0008, - 0x318c: 0x0008, 0x318d: 0x0008, 0x318e: 0x0008, 0x318f: 0x0008, 0x3190: 0x0008, 0x3191: 0x0008, - 0x3192: 0x0008, 0x3193: 0x0008, 0x3194: 0x0008, 0x3195: 0x0008, 0x3196: 0x0008, 0x3197: 0x0008, - 0x3198: 0x0008, 0x3199: 0x0008, 0x319a: 0x0008, 0x319b: 0x0008, 0x319c: 0x0008, 0x319d: 0x0008, - 0x319e: 0x0008, 0x319f: 0x0008, 0x31a0: 0x0008, 0x31a1: 0x0008, 0x31a2: 0x0008, 0x31a3: 0x0008, - 0x31a4: 0x0008, 0x31a5: 0x0008, 0x31a6: 0x0200, 0x31a7: 0x0200, 0x31a8: 0x0200, 0x31a9: 0x0200, - 0x31aa: 0x0200, 0x31ab: 0x0200, 0x31ac: 0x0200, 0x31ad: 0x0200, 0x31ae: 0x0200, 0x31af: 0x0200, - 0x31b0: 0x0200, 0x31b1: 0x0200, 0x31b2: 0x0200, 0x31b3: 0x0200, 0x31b4: 0x0200, 0x31b5: 0x0200, - 0x31b6: 0x0200, 0x31b7: 0x0200, 0x31b8: 0x0200, 0x31b9: 0x0200, 0x31ba: 0x0200, 0x31bb: 0x0200, - 0x31bc: 0x0200, 0x31bd: 0x0200, 0x31be: 0x0200, 0x31bf: 0x0200, + 0x318f: 0x0024, // Block 0xc7, offset 0x31c0 - 0x31c1: 0x0008, 0x31c2: 0x0008, 0x31c3: 0x0008, 0x31c4: 0x0008, 0x31c5: 0x0008, - 0x31c6: 0x0008, 0x31c7: 0x0008, 0x31c8: 0x0008, 0x31c9: 0x0008, 0x31ca: 0x0008, 0x31cb: 0x0008, - 0x31cc: 0x0008, 0x31cd: 0x0008, 0x31ce: 0x0008, 0x31cf: 0x0008, - 0x31da: 0x0008, - 0x31ef: 0x0008, - 0x31f2: 0x0008, 0x31f3: 0x0008, 0x31f4: 0x0008, 0x31f5: 0x0008, - 0x31f6: 0x0008, 0x31f7: 0x0008, 0x31f8: 0x0008, 0x31f9: 0x0008, 0x31fa: 0x0008, - 0x31fc: 0x0008, 0x31fd: 0x0008, 0x31fe: 0x0008, 0x31ff: 0x0008, + 0x31ee: 0x0024, // Block 0xc8, offset 0x3200 - 0x3209: 0x0008, 0x320a: 0x0008, 0x320b: 0x0008, - 0x320c: 0x0008, 0x320d: 0x0008, 0x320e: 0x0008, 0x320f: 0x0008, 0x3210: 0x0008, 0x3211: 0x0008, - 0x3212: 0x0008, 0x3213: 0x0008, 0x3214: 0x0008, 0x3215: 0x0008, 0x3216: 0x0008, 0x3217: 0x0008, - 0x3218: 0x0008, 0x3219: 0x0008, 0x321a: 0x0008, 0x321b: 0x0008, 0x321c: 0x0008, 0x321d: 0x0008, - 0x321e: 0x0008, 0x321f: 0x0008, 0x3220: 0x0008, 0x3221: 0x0008, 0x3222: 0x0008, 0x3223: 0x0008, - 0x3224: 0x0008, 0x3225: 0x0008, 0x3226: 0x0008, 0x3227: 0x0008, 0x3228: 0x0008, 0x3229: 0x0008, - 0x322a: 0x0008, 0x322b: 0x0008, 0x322c: 0x0008, 0x322d: 0x0008, 0x322e: 0x0008, 0x322f: 0x0008, - 0x3230: 0x0008, 0x3231: 0x0008, 0x3232: 0x0008, 0x3233: 0x0008, 0x3234: 0x0008, 0x3235: 0x0008, - 0x3236: 0x0008, 0x3237: 0x0008, 0x3238: 0x0008, 0x3239: 0x0008, 0x323a: 0x0008, 0x323b: 0x0008, - 0x323c: 0x0008, 0x323d: 0x0008, 0x323e: 0x0008, 0x323f: 0x0008, + 0x322c: 0x0024, 0x322d: 0x0024, 0x322e: 0x0024, 0x322f: 0x0024, // Block 0xc9, offset 0x3240 - 0x3240: 0x0008, 0x3241: 0x0008, 0x3242: 0x0008, 0x3243: 0x0008, 0x3244: 0x0008, 0x3245: 0x0008, - 0x3246: 0x0008, 0x3247: 0x0008, 0x3248: 0x0008, 0x3249: 0x0008, 0x324a: 0x0008, 0x324b: 0x0008, - 0x324c: 0x0008, 0x324d: 0x0008, 0x324e: 0x0008, 0x324f: 0x0008, 0x3250: 0x0008, 0x3251: 0x0008, - 0x3252: 0x0008, 0x3253: 0x0008, 0x3254: 0x0008, 0x3255: 0x0008, 0x3256: 0x0008, 0x3257: 0x0008, - 0x3258: 0x0008, 0x3259: 0x0008, 0x325a: 0x0008, 0x325b: 0x0008, 0x325c: 0x0008, 0x325d: 0x0008, - 0x325e: 0x0008, 0x325f: 0x0008, 0x3260: 0x0008, 0x3261: 0x0008, 0x3262: 0x0008, 0x3263: 0x0008, - 0x3264: 0x0008, 0x3265: 0x0008, 0x3266: 0x0008, 0x3267: 0x0008, 0x3268: 0x0008, 0x3269: 0x0008, - 0x326a: 0x0008, 0x326b: 0x0008, 0x326c: 0x0008, 0x326d: 0x0008, 0x326e: 0x0008, 0x326f: 0x0008, - 0x3270: 0x0008, 0x3271: 0x0008, 0x3272: 0x0008, 0x3273: 0x0008, 0x3274: 0x0008, 0x3275: 0x0008, - 0x3276: 0x0008, 0x3277: 0x0008, 0x3278: 0x0008, 0x3279: 0x0008, 0x327a: 0x0008, 0x327b: 0x0004, - 0x327c: 0x0004, 0x327d: 0x0004, 0x327e: 0x0004, 0x327f: 0x0004, + 0x326e: 0x0024, 0x326f: 0x0024, // Block 0xca, offset 0x3280 - 0x3280: 0x0008, 0x3281: 0x0008, 0x3282: 0x0008, 0x3283: 0x0008, 0x3284: 0x0008, 0x3285: 0x0008, - 0x3286: 0x0008, 0x3287: 0x0008, 0x3288: 0x0008, 0x3289: 0x0008, 0x328a: 0x0008, 0x328b: 0x0008, - 0x328c: 0x0008, 0x328d: 0x0008, 0x328e: 0x0008, 0x328f: 0x0008, 0x3290: 0x0008, 0x3291: 0x0008, - 0x3292: 0x0008, 0x3293: 0x0008, 0x3294: 0x0008, 0x3295: 0x0008, 0x3296: 0x0008, 0x3297: 0x0008, - 0x3298: 0x0008, 0x3299: 0x0008, 0x329a: 0x0008, 0x329b: 0x0008, 0x329c: 0x0008, 0x329d: 0x0008, - 0x329e: 0x0008, 0x329f: 0x0008, 0x32a0: 0x0008, 0x32a1: 0x0008, 0x32a2: 0x0008, 0x32a3: 0x0008, - 0x32a4: 0x0008, 0x32a5: 0x0008, 0x32a6: 0x0008, 0x32a7: 0x0008, 0x32a8: 0x0008, 0x32a9: 0x0008, - 0x32aa: 0x0008, 0x32ab: 0x0008, 0x32ac: 0x0008, 0x32ad: 0x0008, 0x32ae: 0x0008, 0x32af: 0x0008, - 0x32b0: 0x0008, 0x32b1: 0x0008, 0x32b2: 0x0008, 0x32b3: 0x0008, 0x32b4: 0x0008, 0x32b5: 0x0008, - 0x32b6: 0x0008, 0x32b7: 0x0008, 0x32b8: 0x0008, 0x32b9: 0x0008, 0x32ba: 0x0008, 0x32bb: 0x0008, - 0x32bc: 0x0008, 0x32bd: 0x0008, + 0x32a3: 0x0024, + 0x32a6: 0x0024, + 0x32ae: 0x0024, 0x32af: 0x0024, + 0x32b5: 0x0024, // Block 0xcb, offset 0x32c0 - 0x32c6: 0x0008, 0x32c7: 0x0008, 0x32c8: 0x0008, 0x32c9: 0x0008, 0x32ca: 0x0008, 0x32cb: 0x0008, - 0x32cc: 0x0008, 0x32cd: 0x0008, 0x32ce: 0x0008, 0x32cf: 0x0008, 0x32d0: 0x0008, 0x32d1: 0x0008, - 0x32d2: 0x0008, 0x32d3: 0x0008, 0x32d4: 0x0008, 0x32d5: 0x0008, 0x32d6: 0x0008, 0x32d7: 0x0008, - 0x32d8: 0x0008, 0x32d9: 0x0008, 0x32da: 0x0008, 0x32db: 0x0008, 0x32dc: 0x0008, 0x32dd: 0x0008, - 0x32de: 0x0008, 0x32df: 0x0008, 0x32e0: 0x0008, 0x32e1: 0x0008, 0x32e2: 0x0008, 0x32e3: 0x0008, - 0x32e4: 0x0008, 0x32e5: 0x0008, 0x32e6: 0x0008, 0x32e7: 0x0008, 0x32e8: 0x0008, 0x32e9: 0x0008, - 0x32ea: 0x0008, 0x32eb: 0x0008, 0x32ec: 0x0008, 0x32ed: 0x0008, 0x32ee: 0x0008, 0x32ef: 0x0008, - 0x32f0: 0x0008, 0x32f1: 0x0008, 0x32f2: 0x0008, 0x32f3: 0x0008, 0x32f4: 0x0008, 0x32f5: 0x0008, - 0x32f6: 0x0008, 0x32f7: 0x0008, 0x32f8: 0x0008, 0x32f9: 0x0008, 0x32fa: 0x0008, 0x32fb: 0x0008, - 0x32fc: 0x0008, 0x32fd: 0x0008, 0x32fe: 0x0008, 0x32ff: 0x0008, + 0x32d0: 0x0024, 0x32d1: 0x0024, + 0x32d2: 0x0024, 0x32d3: 0x0024, 0x32d4: 0x0024, 0x32d5: 0x0024, 0x32d6: 0x0024, // Block 0xcc, offset 0x3300 - 0x3300: 0x0008, 0x3301: 0x0008, 0x3302: 0x0008, 0x3303: 0x0008, 0x3304: 0x0008, 0x3305: 0x0008, - 0x3306: 0x0008, 0x3307: 0x0008, 0x3308: 0x0008, 0x3309: 0x0008, 0x330a: 0x0008, 0x330b: 0x0008, - 0x330c: 0x0008, 0x330d: 0x0008, 0x330e: 0x0008, 0x330f: 0x0008, + 0x3304: 0x0024, 0x3305: 0x0024, + 0x3306: 0x0024, 0x3307: 0x0024, 0x3308: 0x0024, 0x3309: 0x0024, 0x330a: 0x0024, // Block 0xcd, offset 0x3340 - 0x3374: 0x0008, 0x3375: 0x0008, - 0x3376: 0x0008, 0x3377: 0x0008, 0x3378: 0x0008, 0x3379: 0x0008, 0x337a: 0x0008, 0x337b: 0x0008, - 0x337c: 0x0008, 0x337d: 0x0008, 0x337e: 0x0008, 0x337f: 0x0008, + 0x3344: 0x0008, + 0x336c: 0x0008, 0x336d: 0x0008, 0x336e: 0x0008, 0x336f: 0x0008, // Block 0xce, offset 0x3380 - 0x3395: 0x0008, 0x3396: 0x0008, 0x3397: 0x0008, + 0x3394: 0x0008, 0x3395: 0x0008, 0x3396: 0x0008, 0x3397: 0x0008, 0x3398: 0x0008, 0x3399: 0x0008, 0x339a: 0x0008, 0x339b: 0x0008, 0x339c: 0x0008, 0x339d: 0x0008, - 0x339e: 0x0008, 0x339f: 0x0008, 0x33a0: 0x0008, 0x33a1: 0x0008, 0x33a2: 0x0008, 0x33a3: 0x0008, - 0x33a4: 0x0008, 0x33a5: 0x0008, 0x33a6: 0x0008, 0x33a7: 0x0008, 0x33a8: 0x0008, 0x33a9: 0x0008, - 0x33aa: 0x0008, 0x33ab: 0x0008, 0x33ac: 0x0008, 0x33ad: 0x0008, 0x33ae: 0x0008, 0x33af: 0x0008, - 0x33b0: 0x0008, 0x33b1: 0x0008, 0x33b2: 0x0008, 0x33b3: 0x0008, 0x33b4: 0x0008, 0x33b5: 0x0008, - 0x33b6: 0x0008, 0x33b7: 0x0008, 0x33b8: 0x0008, 0x33b9: 0x0008, 0x33ba: 0x0008, 0x33bb: 0x0008, - 0x33bc: 0x0008, 0x33bd: 0x0008, 0x33be: 0x0008, 0x33bf: 0x0008, + 0x339e: 0x0008, 0x339f: 0x0008, + 0x33af: 0x0008, + 0x33b0: 0x0008, // Block 0xcf, offset 0x33c0 - 0x33cc: 0x0008, 0x33cd: 0x0008, 0x33ce: 0x0008, 0x33cf: 0x0008, + 0x33c0: 0x0008, + 0x33cf: 0x0008, 0x33d0: 0x0008, + 0x33f6: 0x0008, 0x33f7: 0x0008, 0x33f8: 0x0008, 0x33f9: 0x0008, 0x33fa: 0x0008, 0x33fb: 0x0008, + 0x33fc: 0x0008, 0x33fd: 0x0008, 0x33fe: 0x0008, 0x33ff: 0x0008, // Block 0xd0, offset 0x3400 - 0x3408: 0x0008, 0x3409: 0x0008, 0x340a: 0x0008, 0x340b: 0x0008, - 0x340c: 0x0008, 0x340d: 0x0008, 0x340e: 0x0008, 0x340f: 0x0008, - 0x341a: 0x0008, 0x341b: 0x0008, 0x341c: 0x0008, 0x341d: 0x0008, - 0x341e: 0x0008, 0x341f: 0x0008, + 0x3430: 0x0008, 0x3431: 0x0008, + 0x343e: 0x0008, 0x343f: 0x0008, // Block 0xd1, offset 0x3440 - 0x3448: 0x0008, 0x3449: 0x0008, 0x344a: 0x0008, 0x344b: 0x0008, - 0x344c: 0x0008, 0x344d: 0x0008, 0x344e: 0x0008, 0x344f: 0x0008, + 0x344e: 0x0008, 0x3451: 0x0008, + 0x3452: 0x0008, 0x3453: 0x0008, 0x3454: 0x0008, 0x3455: 0x0008, 0x3456: 0x0008, 0x3457: 0x0008, + 0x3458: 0x0008, 0x3459: 0x0008, 0x345a: 0x0008, 0x346e: 0x0008, 0x346f: 0x0008, 0x3470: 0x0008, 0x3471: 0x0008, 0x3472: 0x0008, 0x3473: 0x0008, 0x3474: 0x0008, 0x3475: 0x0008, 0x3476: 0x0008, 0x3477: 0x0008, 0x3478: 0x0008, 0x3479: 0x0008, 0x347a: 0x0008, 0x347b: 0x0008, 0x347c: 0x0008, 0x347d: 0x0008, 0x347e: 0x0008, 0x347f: 0x0008, // Block 0xd2, offset 0x3480 + 0x3480: 0x0008, 0x3481: 0x0008, 0x3482: 0x0008, 0x3483: 0x0008, 0x3484: 0x0008, 0x3485: 0x0008, + 0x3486: 0x0008, 0x3487: 0x0008, 0x3488: 0x0008, 0x3489: 0x0008, 0x348a: 0x0008, 0x348b: 0x0008, 0x348c: 0x0008, 0x348d: 0x0008, 0x348e: 0x0008, 0x348f: 0x0008, 0x3490: 0x0008, 0x3491: 0x0008, 0x3492: 0x0008, 0x3493: 0x0008, 0x3494: 0x0008, 0x3495: 0x0008, 0x3496: 0x0008, 0x3497: 0x0008, 0x3498: 0x0008, 0x3499: 0x0008, 0x349a: 0x0008, 0x349b: 0x0008, 0x349c: 0x0008, 0x349d: 0x0008, 0x349e: 0x0008, 0x349f: 0x0008, 0x34a0: 0x0008, 0x34a1: 0x0008, 0x34a2: 0x0008, 0x34a3: 0x0008, - 0x34a4: 0x0008, 0x34a5: 0x0008, 0x34a6: 0x0008, 0x34a7: 0x0008, 0x34a8: 0x0008, 0x34a9: 0x0008, - 0x34aa: 0x0008, 0x34ab: 0x0008, 0x34ac: 0x0008, 0x34ad: 0x0008, 0x34ae: 0x0008, 0x34af: 0x0008, - 0x34b0: 0x0008, 0x34b1: 0x0008, 0x34b2: 0x0008, 0x34b3: 0x0008, 0x34b4: 0x0008, 0x34b5: 0x0008, - 0x34b6: 0x0008, 0x34b7: 0x0008, 0x34b8: 0x0008, 0x34b9: 0x0008, 0x34ba: 0x0008, - 0x34bc: 0x0008, 0x34bd: 0x0008, 0x34be: 0x0008, 0x34bf: 0x0008, + 0x34a4: 0x0008, 0x34a5: 0x0008, 0x34a6: 0x1000, 0x34a7: 0x1000, 0x34a8: 0x1000, 0x34a9: 0x1000, + 0x34aa: 0x1000, 0x34ab: 0x1000, 0x34ac: 0x1000, 0x34ad: 0x1000, 0x34ae: 0x1000, 0x34af: 0x1000, + 0x34b0: 0x1000, 0x34b1: 0x1000, 0x34b2: 0x1000, 0x34b3: 0x1000, 0x34b4: 0x1000, 0x34b5: 0x1000, + 0x34b6: 0x1000, 0x34b7: 0x1000, 0x34b8: 0x1000, 0x34b9: 0x1000, 0x34ba: 0x1000, 0x34bb: 0x1000, + 0x34bc: 0x1000, 0x34bd: 0x1000, 0x34be: 0x1000, 0x34bf: 0x1000, // Block 0xd3, offset 0x34c0 - 0x34c0: 0x0008, 0x34c1: 0x0008, 0x34c2: 0x0008, 0x34c3: 0x0008, 0x34c4: 0x0008, 0x34c5: 0x0008, - 0x34c7: 0x0008, 0x34c8: 0x0008, 0x34c9: 0x0008, 0x34ca: 0x0008, 0x34cb: 0x0008, - 0x34cc: 0x0008, 0x34cd: 0x0008, 0x34ce: 0x0008, 0x34cf: 0x0008, 0x34d0: 0x0008, 0x34d1: 0x0008, - 0x34d2: 0x0008, 0x34d3: 0x0008, 0x34d4: 0x0008, 0x34d5: 0x0008, 0x34d6: 0x0008, 0x34d7: 0x0008, - 0x34d8: 0x0008, 0x34d9: 0x0008, 0x34da: 0x0008, 0x34db: 0x0008, 0x34dc: 0x0008, 0x34dd: 0x0008, - 0x34de: 0x0008, 0x34df: 0x0008, 0x34e0: 0x0008, 0x34e1: 0x0008, 0x34e2: 0x0008, 0x34e3: 0x0008, - 0x34e4: 0x0008, 0x34e5: 0x0008, 0x34e6: 0x0008, 0x34e7: 0x0008, 0x34e8: 0x0008, 0x34e9: 0x0008, - 0x34ea: 0x0008, 0x34eb: 0x0008, 0x34ec: 0x0008, 0x34ed: 0x0008, 0x34ee: 0x0008, 0x34ef: 0x0008, - 0x34f0: 0x0008, 0x34f1: 0x0008, 0x34f2: 0x0008, 0x34f3: 0x0008, 0x34f4: 0x0008, 0x34f5: 0x0008, - 0x34f6: 0x0008, 0x34f7: 0x0008, 0x34f8: 0x0008, 0x34f9: 0x0008, 0x34fa: 0x0008, 0x34fb: 0x0008, + 0x34c1: 0x0008, 0x34c2: 0x0008, 0x34c3: 0x0008, 0x34c4: 0x0008, 0x34c5: 0x0008, + 0x34c6: 0x0008, 0x34c7: 0x0008, 0x34c8: 0x0008, 0x34c9: 0x0008, 0x34ca: 0x0008, 0x34cb: 0x0008, + 0x34cc: 0x0008, 0x34cd: 0x0008, 0x34ce: 0x0008, 0x34cf: 0x0008, + 0x34da: 0x0008, + 0x34ef: 0x0008, + 0x34f2: 0x0008, 0x34f3: 0x0008, 0x34f4: 0x0008, 0x34f5: 0x0008, + 0x34f6: 0x0008, 0x34f7: 0x0008, 0x34f8: 0x0008, 0x34f9: 0x0008, 0x34fa: 0x0008, 0x34fc: 0x0008, 0x34fd: 0x0008, 0x34fe: 0x0008, 0x34ff: 0x0008, // Block 0xd4, offset 0x3500 - 0x3500: 0x0002, 0x3501: 0x0002, 0x3502: 0x0002, 0x3503: 0x0002, 0x3504: 0x0002, 0x3505: 0x0002, - 0x3506: 0x0002, 0x3507: 0x0002, 0x3508: 0x0002, 0x3509: 0x0002, 0x350a: 0x0002, 0x350b: 0x0002, - 0x350c: 0x0002, 0x350d: 0x0002, 0x350e: 0x0002, 0x350f: 0x0002, 0x3510: 0x0002, 0x3511: 0x0002, - 0x3512: 0x0002, 0x3513: 0x0002, 0x3514: 0x0002, 0x3515: 0x0002, 0x3516: 0x0002, 0x3517: 0x0002, - 0x3518: 0x0002, 0x3519: 0x0002, 0x351a: 0x0002, 0x351b: 0x0002, 0x351c: 0x0002, 0x351d: 0x0002, - 0x351e: 0x0002, 0x351f: 0x0002, 0x3520: 0x0004, 0x3521: 0x0004, 0x3522: 0x0004, 0x3523: 0x0004, - 0x3524: 0x0004, 0x3525: 0x0004, 0x3526: 0x0004, 0x3527: 0x0004, 0x3528: 0x0004, 0x3529: 0x0004, - 0x352a: 0x0004, 0x352b: 0x0004, 0x352c: 0x0004, 0x352d: 0x0004, 0x352e: 0x0004, 0x352f: 0x0004, - 0x3530: 0x0004, 0x3531: 0x0004, 0x3532: 0x0004, 0x3533: 0x0004, 0x3534: 0x0004, 0x3535: 0x0004, - 0x3536: 0x0004, 0x3537: 0x0004, 0x3538: 0x0004, 0x3539: 0x0004, 0x353a: 0x0004, 0x353b: 0x0004, - 0x353c: 0x0004, 0x353d: 0x0004, 0x353e: 0x0004, 0x353f: 0x0004, + 0x3509: 0x0008, 0x350a: 0x0008, 0x350b: 0x0008, + 0x350c: 0x0008, 0x350d: 0x0008, 0x350e: 0x0008, 0x350f: 0x0008, 0x3510: 0x0008, 0x3511: 0x0008, + 0x3512: 0x0008, 0x3513: 0x0008, 0x3514: 0x0008, 0x3515: 0x0008, 0x3516: 0x0008, 0x3517: 0x0008, + 0x3518: 0x0008, 0x3519: 0x0008, 0x351a: 0x0008, 0x351b: 0x0008, 0x351c: 0x0008, 0x351d: 0x0008, + 0x351e: 0x0008, 0x351f: 0x0008, + 0x3526: 0x0008, 0x3527: 0x0008, 0x3528: 0x0008, 0x3529: 0x0008, + 0x352a: 0x0008, 0x352b: 0x0008, 0x352c: 0x0008, 0x352d: 0x0008, 0x352e: 0x0008, 0x352f: 0x0008, + 0x3530: 0x0008, 0x3531: 0x0008, 0x3532: 0x0008, 0x3533: 0x0008, 0x3534: 0x0008, 0x3535: 0x0008, + 0x3536: 0x0008, 0x3537: 0x0008, 0x3538: 0x0008, 0x3539: 0x0008, 0x353a: 0x0008, 0x353b: 0x0008, + 0x353c: 0x0008, 0x353d: 0x0008, 0x353e: 0x0008, 0x353f: 0x0008, // Block 0xd5, offset 0x3540 - 0x3540: 0x0002, 0x3541: 0x0002, 0x3542: 0x0002, 0x3543: 0x0002, 0x3544: 0x0002, 0x3545: 0x0002, - 0x3546: 0x0002, 0x3547: 0x0002, 0x3548: 0x0002, 0x3549: 0x0002, 0x354a: 0x0002, 0x354b: 0x0002, - 0x354c: 0x0002, 0x354d: 0x0002, 0x354e: 0x0002, 0x354f: 0x0002, 0x3550: 0x0002, 0x3551: 0x0002, - 0x3552: 0x0002, 0x3553: 0x0002, 0x3554: 0x0002, 0x3555: 0x0002, 0x3556: 0x0002, 0x3557: 0x0002, - 0x3558: 0x0002, 0x3559: 0x0002, 0x355a: 0x0002, 0x355b: 0x0002, 0x355c: 0x0002, 0x355d: 0x0002, - 0x355e: 0x0002, 0x355f: 0x0002, 0x3560: 0x0002, 0x3561: 0x0002, 0x3562: 0x0002, 0x3563: 0x0002, - 0x3564: 0x0002, 0x3565: 0x0002, 0x3566: 0x0002, 0x3567: 0x0002, 0x3568: 0x0002, 0x3569: 0x0002, - 0x356a: 0x0002, 0x356b: 0x0002, 0x356c: 0x0002, 0x356d: 0x0002, 0x356e: 0x0002, 0x356f: 0x0002, - 0x3570: 0x0002, 0x3571: 0x0002, 0x3572: 0x0002, 0x3573: 0x0002, 0x3574: 0x0002, 0x3575: 0x0002, - 0x3576: 0x0002, 0x3577: 0x0002, 0x3578: 0x0002, 0x3579: 0x0002, 0x357a: 0x0002, 0x357b: 0x0002, - 0x357c: 0x0002, 0x357d: 0x0002, 0x357e: 0x0002, 0x357f: 0x0002, + 0x3540: 0x0008, 0x3541: 0x0008, 0x3542: 0x0008, 0x3543: 0x0008, 0x3544: 0x0008, 0x3545: 0x0008, + 0x3546: 0x0008, 0x3547: 0x0008, 0x3548: 0x0008, 0x3549: 0x0008, 0x354a: 0x0008, 0x354b: 0x0008, + 0x354c: 0x0008, 0x354d: 0x0008, 0x354e: 0x0008, 0x354f: 0x0008, 0x3550: 0x0008, 0x3551: 0x0008, + 0x3552: 0x0008, 0x3553: 0x0008, 0x3554: 0x0008, 0x3555: 0x0008, 0x3556: 0x0008, 0x3557: 0x0008, + 0x3558: 0x0008, 0x3559: 0x0008, 0x355a: 0x0008, 0x355b: 0x0008, 0x355c: 0x0008, 0x355d: 0x0008, + 0x355e: 0x0008, 0x355f: 0x0008, 0x3560: 0x0008, 0x3561: 0x0008, 0x3562: 0x0008, 0x3563: 0x0008, + 0x3564: 0x0008, 0x3565: 0x0008, 0x3566: 0x0008, 0x3567: 0x0008, 0x3568: 0x0008, 0x3569: 0x0008, + 0x356a: 0x0008, 0x356b: 0x0008, 0x356c: 0x0008, 0x356d: 0x0008, 0x356e: 0x0008, 0x356f: 0x0008, + 0x3570: 0x0008, 0x3571: 0x0008, 0x3572: 0x0008, 0x3573: 0x0008, 0x3574: 0x0008, 0x3575: 0x0008, + 0x3576: 0x0008, 0x3577: 0x0008, 0x3578: 0x0008, 0x3579: 0x0008, 0x357a: 0x0008, 0x357b: 0x0008, + 0x357c: 0x0008, 0x357d: 0x0008, 0x357e: 0x0008, 0x357f: 0x0008, // Block 0xd6, offset 0x3580 - 0x3580: 0x0004, 0x3581: 0x0004, 0x3582: 0x0004, 0x3583: 0x0004, 0x3584: 0x0004, 0x3585: 0x0004, - 0x3586: 0x0004, 0x3587: 0x0004, 0x3588: 0x0004, 0x3589: 0x0004, 0x358a: 0x0004, 0x358b: 0x0004, - 0x358c: 0x0004, 0x358d: 0x0004, 0x358e: 0x0004, 0x358f: 0x0004, 0x3590: 0x0004, 0x3591: 0x0004, - 0x3592: 0x0004, 0x3593: 0x0004, 0x3594: 0x0004, 0x3595: 0x0004, 0x3596: 0x0004, 0x3597: 0x0004, - 0x3598: 0x0004, 0x3599: 0x0004, 0x359a: 0x0004, 0x359b: 0x0004, 0x359c: 0x0004, 0x359d: 0x0004, - 0x359e: 0x0004, 0x359f: 0x0004, 0x35a0: 0x0004, 0x35a1: 0x0004, 0x35a2: 0x0004, 0x35a3: 0x0004, - 0x35a4: 0x0004, 0x35a5: 0x0004, 0x35a6: 0x0004, 0x35a7: 0x0004, 0x35a8: 0x0004, 0x35a9: 0x0004, - 0x35aa: 0x0004, 0x35ab: 0x0004, 0x35ac: 0x0004, 0x35ad: 0x0004, 0x35ae: 0x0004, 0x35af: 0x0004, - 0x35b0: 0x0002, 0x35b1: 0x0002, 0x35b2: 0x0002, 0x35b3: 0x0002, 0x35b4: 0x0002, 0x35b5: 0x0002, - 0x35b6: 0x0002, 0x35b7: 0x0002, 0x35b8: 0x0002, 0x35b9: 0x0002, 0x35ba: 0x0002, 0x35bb: 0x0002, - 0x35bc: 0x0002, 0x35bd: 0x0002, 0x35be: 0x0002, 0x35bf: 0x0002, + 0x3580: 0x0008, 0x3581: 0x0008, 0x3582: 0x0008, 0x3583: 0x0008, 0x3584: 0x0008, 0x3585: 0x0008, + 0x3586: 0x0008, 0x3587: 0x0008, 0x3588: 0x0008, 0x3589: 0x0008, 0x358a: 0x0008, 0x358b: 0x0008, + 0x358c: 0x0008, 0x358d: 0x0008, 0x358e: 0x0008, 0x358f: 0x0008, 0x3590: 0x0008, 0x3591: 0x0008, + 0x3592: 0x0008, 0x3593: 0x0008, 0x3594: 0x0008, 0x3595: 0x0008, 0x3596: 0x0008, 0x3597: 0x0008, + 0x3598: 0x0008, 0x3599: 0x0008, 0x359a: 0x0008, 0x359b: 0x0008, 0x359c: 0x0008, 0x359d: 0x0008, + 0x359e: 0x0008, 0x359f: 0x0008, 0x35a0: 0x0008, 0x35a1: 0x0008, + 0x35a4: 0x0008, 0x35a5: 0x0008, 0x35a6: 0x0008, 0x35a7: 0x0008, 0x35a8: 0x0008, 0x35a9: 0x0008, + 0x35aa: 0x0008, 0x35ab: 0x0008, 0x35ac: 0x0008, 0x35ad: 0x0008, 0x35ae: 0x0008, 0x35af: 0x0008, + 0x35b0: 0x0008, 0x35b1: 0x0008, 0x35b2: 0x0008, 0x35b3: 0x0008, 0x35b4: 0x0008, 0x35b5: 0x0008, + 0x35b6: 0x0008, 0x35b7: 0x0008, 0x35b8: 0x0008, 0x35b9: 0x0008, 0x35ba: 0x0008, 0x35bb: 0x0008, + 0x35bc: 0x0008, 0x35bd: 0x0008, 0x35be: 0x0008, 0x35bf: 0x0008, + // Block 0xd7, offset 0x35c0 + 0x35c0: 0x0008, 0x35c1: 0x0008, 0x35c2: 0x0008, 0x35c3: 0x0008, 0x35c4: 0x0008, 0x35c5: 0x0008, + 0x35c6: 0x0008, 0x35c7: 0x0008, 0x35c8: 0x0008, 0x35c9: 0x0008, 0x35ca: 0x0008, 0x35cb: 0x0008, + 0x35cc: 0x0008, 0x35cd: 0x0008, 0x35ce: 0x0008, 0x35cf: 0x0008, 0x35d0: 0x0008, 0x35d1: 0x0008, + 0x35d2: 0x0008, 0x35d3: 0x0008, 0x35d6: 0x0008, 0x35d7: 0x0008, + 0x35d9: 0x0008, 0x35da: 0x0008, 0x35db: 0x0008, + 0x35de: 0x0008, 0x35df: 0x0008, 0x35e0: 0x0008, 0x35e1: 0x0008, 0x35e2: 0x0008, 0x35e3: 0x0008, + 0x35e4: 0x0008, 0x35e5: 0x0008, 0x35e6: 0x0008, 0x35e7: 0x0008, 0x35e8: 0x0008, 0x35e9: 0x0008, + 0x35ea: 0x0008, 0x35eb: 0x0008, 0x35ec: 0x0008, 0x35ed: 0x0008, 0x35ee: 0x0008, 0x35ef: 0x0008, + 0x35f0: 0x0008, 0x35f1: 0x0008, 0x35f2: 0x0008, 0x35f3: 0x0008, 0x35f4: 0x0008, 0x35f5: 0x0008, + 0x35f6: 0x0008, 0x35f7: 0x0008, 0x35f8: 0x0008, 0x35f9: 0x0008, 0x35fa: 0x0008, 0x35fb: 0x0008, + 0x35fc: 0x0008, 0x35fd: 0x0008, 0x35fe: 0x0008, 0x35ff: 0x0008, + // Block 0xd8, offset 0x3600 + 0x3600: 0x0008, 0x3601: 0x0008, 0x3602: 0x0008, 0x3603: 0x0008, 0x3604: 0x0008, 0x3605: 0x0008, + 0x3606: 0x0008, 0x3607: 0x0008, 0x3608: 0x0008, 0x3609: 0x0008, 0x360a: 0x0008, 0x360b: 0x0008, + 0x360c: 0x0008, 0x360d: 0x0008, 0x360e: 0x0008, 0x360f: 0x0008, 0x3610: 0x0008, 0x3611: 0x0008, + 0x3612: 0x0008, 0x3613: 0x0008, 0x3614: 0x0008, 0x3615: 0x0008, 0x3616: 0x0008, 0x3617: 0x0008, + 0x3618: 0x0008, 0x3619: 0x0008, 0x361a: 0x0008, 0x361b: 0x0008, 0x361c: 0x0008, 0x361d: 0x0008, + 0x361e: 0x0008, 0x361f: 0x0008, 0x3620: 0x0008, 0x3621: 0x0008, 0x3622: 0x0008, 0x3623: 0x0008, + 0x3624: 0x0008, 0x3625: 0x0008, 0x3626: 0x0008, 0x3627: 0x0008, 0x3628: 0x0008, 0x3629: 0x0008, + 0x362a: 0x0008, 0x362b: 0x0008, 0x362c: 0x0008, 0x362d: 0x0008, 0x362e: 0x0008, 0x362f: 0x0008, + 0x3630: 0x0008, 0x3633: 0x0008, 0x3634: 0x0008, 0x3635: 0x0008, + 0x3637: 0x0008, 0x3638: 0x0008, 0x3639: 0x0008, 0x363a: 0x0008, 0x363b: 0x0024, + 0x363c: 0x0024, 0x363d: 0x0024, 0x363e: 0x0024, 0x363f: 0x0024, + // Block 0xd9, offset 0x3640 + 0x3640: 0x0008, 0x3641: 0x0008, 0x3642: 0x0008, 0x3643: 0x0008, 0x3644: 0x0008, 0x3645: 0x0008, + 0x3646: 0x0008, 0x3647: 0x0008, 0x3648: 0x0008, 0x3649: 0x0008, 0x364a: 0x0008, 0x364b: 0x0008, + 0x364c: 0x0008, 0x364d: 0x0008, 0x364e: 0x0008, 0x364f: 0x0008, 0x3650: 0x0008, 0x3651: 0x0008, + 0x3652: 0x0008, 0x3653: 0x0008, 0x3654: 0x0008, 0x3655: 0x0008, 0x3656: 0x0008, 0x3657: 0x0008, + 0x3658: 0x0008, 0x3659: 0x0008, 0x365a: 0x0008, 0x365b: 0x0008, 0x365c: 0x0008, 0x365d: 0x0008, + 0x365e: 0x0008, 0x365f: 0x0008, 0x3660: 0x0008, 0x3661: 0x0008, 0x3662: 0x0008, 0x3663: 0x0008, + 0x3664: 0x0008, 0x3665: 0x0008, 0x3666: 0x0008, 0x3667: 0x0008, 0x3668: 0x0008, 0x3669: 0x0008, + 0x366a: 0x0008, 0x366b: 0x0008, 0x366c: 0x0008, 0x366d: 0x0008, 0x366e: 0x0008, 0x366f: 0x0008, + 0x3670: 0x0008, 0x3671: 0x0008, 0x3672: 0x0008, 0x3673: 0x0008, 0x3674: 0x0008, 0x3675: 0x0008, + 0x3676: 0x0008, 0x3677: 0x0008, 0x3678: 0x0008, 0x3679: 0x0008, 0x367a: 0x0008, 0x367b: 0x0008, + 0x367c: 0x0008, 0x367d: 0x0008, 0x367f: 0x0008, + // Block 0xda, offset 0x3680 + 0x3680: 0x0008, 0x3681: 0x0008, 0x3682: 0x0008, 0x3683: 0x0008, 0x3684: 0x0008, 0x3685: 0x0008, + 0x3686: 0x0008, 0x3687: 0x0008, 0x3688: 0x0008, 0x3689: 0x0008, 0x368a: 0x0008, 0x368b: 0x0008, + 0x368c: 0x0008, 0x368d: 0x0008, 0x368e: 0x0008, 0x368f: 0x0008, 0x3690: 0x0008, 0x3691: 0x0008, + 0x3692: 0x0008, 0x3693: 0x0008, 0x3694: 0x0008, 0x3695: 0x0008, 0x3696: 0x0008, 0x3697: 0x0008, + 0x3698: 0x0008, 0x3699: 0x0008, 0x369a: 0x0008, 0x369b: 0x0008, 0x369c: 0x0008, 0x369d: 0x0008, + 0x369e: 0x0008, 0x369f: 0x0008, 0x36a0: 0x0008, 0x36a1: 0x0008, 0x36a2: 0x0008, 0x36a3: 0x0008, + 0x36a4: 0x0008, 0x36a5: 0x0008, 0x36a6: 0x0008, 0x36a7: 0x0008, 0x36a8: 0x0008, 0x36a9: 0x0008, + 0x36aa: 0x0008, 0x36ab: 0x0008, 0x36ac: 0x0008, 0x36ad: 0x0008, 0x36ae: 0x0008, 0x36af: 0x0008, + 0x36b0: 0x0008, 0x36b1: 0x0008, 0x36b2: 0x0008, 0x36b3: 0x0008, 0x36b4: 0x0008, 0x36b5: 0x0008, + 0x36b6: 0x0008, 0x36b7: 0x0008, 0x36b8: 0x0008, 0x36b9: 0x0008, 0x36ba: 0x0008, 0x36bb: 0x0008, + 0x36bc: 0x0008, 0x36bd: 0x0008, + // Block 0xdb, offset 0x36c0 + 0x36c9: 0x0008, 0x36ca: 0x0008, 0x36cb: 0x0008, + 0x36cc: 0x0008, 0x36cd: 0x0008, 0x36ce: 0x0008, 0x36d0: 0x0008, 0x36d1: 0x0008, + 0x36d2: 0x0008, 0x36d3: 0x0008, 0x36d4: 0x0008, 0x36d5: 0x0008, 0x36d6: 0x0008, 0x36d7: 0x0008, + 0x36d8: 0x0008, 0x36d9: 0x0008, 0x36da: 0x0008, 0x36db: 0x0008, 0x36dc: 0x0008, 0x36dd: 0x0008, + 0x36de: 0x0008, 0x36df: 0x0008, 0x36e0: 0x0008, 0x36e1: 0x0008, 0x36e2: 0x0008, 0x36e3: 0x0008, + 0x36e4: 0x0008, 0x36e5: 0x0008, 0x36e6: 0x0008, 0x36e7: 0x0008, + 0x36ef: 0x0008, + 0x36f0: 0x0008, 0x36f3: 0x0008, 0x36f4: 0x0008, 0x36f5: 0x0008, + 0x36f6: 0x0008, 0x36f7: 0x0008, 0x36f8: 0x0008, 0x36f9: 0x0008, 0x36fa: 0x0008, + // Block 0xdc, offset 0x3700 + 0x3707: 0x0008, 0x370a: 0x0008, 0x370b: 0x0008, + 0x370c: 0x0008, 0x370d: 0x0008, 0x3710: 0x0008, + 0x3715: 0x0008, 0x3716: 0x0008, + 0x3724: 0x0008, 0x3725: 0x0008, 0x3728: 0x0008, + 0x3731: 0x0008, 0x3732: 0x0008, + 0x373c: 0x0008, + // Block 0xdd, offset 0x3740 + 0x3742: 0x0008, 0x3743: 0x0008, 0x3744: 0x0008, + 0x3751: 0x0008, + 0x3752: 0x0008, 0x3753: 0x0008, + 0x375c: 0x0008, 0x375d: 0x0008, + 0x375e: 0x0008, 0x3761: 0x0008, 0x3763: 0x0008, + 0x3768: 0x0008, + 0x376f: 0x0008, + 0x3773: 0x0008, + 0x377a: 0x0008, 0x377b: 0x0008, + 0x377c: 0x0008, 0x377d: 0x0008, 0x377e: 0x0008, 0x377f: 0x0008, + // Block 0xde, offset 0x3780 + 0x3780: 0x0008, 0x3781: 0x0008, 0x3782: 0x0008, 0x3783: 0x0008, 0x3784: 0x0008, 0x3785: 0x0008, + 0x3786: 0x0008, 0x3787: 0x0008, 0x3788: 0x0008, 0x3789: 0x0008, 0x378a: 0x0008, 0x378b: 0x0008, + 0x378c: 0x0008, 0x378d: 0x0008, 0x378e: 0x0008, 0x378f: 0x0008, + // Block 0xdf, offset 0x37c0 + 0x37c0: 0x0008, 0x37c1: 0x0008, 0x37c2: 0x0008, 0x37c3: 0x0008, 0x37c4: 0x0008, 0x37c5: 0x0008, + 0x37cb: 0x0008, + 0x37cc: 0x0008, 0x37cd: 0x0008, 0x37ce: 0x0008, 0x37cf: 0x0008, 0x37d0: 0x0008, 0x37d1: 0x0008, + 0x37d2: 0x0008, 0x37d5: 0x0008, 0x37d6: 0x0008, 0x37d7: 0x0008, + 0x37d8: 0x0008, 0x37d9: 0x0008, 0x37da: 0x0008, 0x37db: 0x0008, 0x37dc: 0x0008, 0x37dd: 0x0008, + 0x37de: 0x0008, 0x37df: 0x0008, 0x37e0: 0x0008, 0x37e1: 0x0008, 0x37e2: 0x0008, 0x37e3: 0x0008, + 0x37e4: 0x0008, 0x37e5: 0x0008, 0x37e9: 0x0008, + 0x37eb: 0x0008, 0x37ec: 0x0008, 0x37ed: 0x0008, 0x37ee: 0x0008, 0x37ef: 0x0008, + 0x37f0: 0x0008, 0x37f3: 0x0008, 0x37f4: 0x0008, 0x37f5: 0x0008, + 0x37f6: 0x0008, 0x37f7: 0x0008, 0x37f8: 0x0008, 0x37f9: 0x0008, 0x37fa: 0x0008, 0x37fb: 0x0008, + 0x37fc: 0x0008, 0x37fd: 0x0008, 0x37fe: 0x0008, 0x37ff: 0x0008, + // Block 0xe0, offset 0x3800 + 0x381a: 0x0008, 0x381b: 0x0008, 0x381c: 0x0008, 0x381d: 0x0008, + 0x381e: 0x0008, 0x381f: 0x0008, 0x3820: 0x0008, 0x3821: 0x0008, 0x3822: 0x0008, 0x3823: 0x0008, + 0x3824: 0x0008, 0x3825: 0x0008, 0x3826: 0x0008, 0x3827: 0x0008, 0x3828: 0x0008, 0x3829: 0x0008, + 0x382a: 0x0008, 0x382b: 0x0008, 0x382c: 0x0008, 0x382d: 0x0008, 0x382e: 0x0008, 0x382f: 0x0008, + 0x3830: 0x0008, 0x3831: 0x0008, 0x3832: 0x0008, 0x3833: 0x0008, 0x3834: 0x0008, 0x3835: 0x0008, + 0x3836: 0x0008, 0x3837: 0x0008, 0x3838: 0x0008, 0x3839: 0x0008, 0x383a: 0x0008, 0x383b: 0x0008, + 0x383c: 0x0008, 0x383d: 0x0008, 0x383e: 0x0008, 0x383f: 0x0008, + // Block 0xe1, offset 0x3840 + 0x384c: 0x0008, 0x384d: 0x0008, 0x384e: 0x0008, 0x384f: 0x0008, + // Block 0xe2, offset 0x3880 + 0x3888: 0x0008, 0x3889: 0x0008, 0x388a: 0x0008, 0x388b: 0x0008, + 0x388c: 0x0008, 0x388d: 0x0008, 0x388e: 0x0008, 0x388f: 0x0008, + 0x389a: 0x0008, 0x389b: 0x0008, 0x389c: 0x0008, 0x389d: 0x0008, + 0x389e: 0x0008, 0x389f: 0x0008, + // Block 0xe3, offset 0x38c0 + 0x38c8: 0x0008, 0x38c9: 0x0008, 0x38ca: 0x0008, 0x38cb: 0x0008, + 0x38cc: 0x0008, 0x38cd: 0x0008, 0x38ce: 0x0008, 0x38cf: 0x0008, + 0x38ee: 0x0008, 0x38ef: 0x0008, + 0x38fc: 0x0008, 0x38fd: 0x0008, 0x38fe: 0x0008, 0x38ff: 0x0008, + // Block 0xe4, offset 0x3900 + 0x3902: 0x0008, 0x3903: 0x0008, 0x3904: 0x0008, 0x3905: 0x0008, + 0x3906: 0x0008, 0x3907: 0x0008, 0x3908: 0x0008, 0x3909: 0x0008, 0x390a: 0x0008, 0x390b: 0x0008, + 0x390c: 0x0008, 0x390d: 0x0008, 0x390e: 0x0008, 0x390f: 0x0008, + 0x3919: 0x0008, 0x391a: 0x0008, 0x391b: 0x0008, 0x391c: 0x0008, 0x391d: 0x0008, + 0x391e: 0x0008, 0x391f: 0x0008, 0x3920: 0x0008, 0x3921: 0x0008, 0x3922: 0x0008, 0x3923: 0x0008, + 0x3924: 0x0008, 0x3925: 0x0008, 0x3926: 0x0008, 0x3927: 0x0008, 0x3928: 0x0008, 0x3929: 0x0008, + 0x392a: 0x0008, 0x392b: 0x0008, 0x392c: 0x0008, 0x392d: 0x0008, 0x392e: 0x0008, 0x392f: 0x0008, + 0x3930: 0x0008, 0x3931: 0x0008, 0x3932: 0x0008, 0x3933: 0x0008, 0x3934: 0x0008, 0x3935: 0x0008, + 0x3936: 0x0008, 0x3937: 0x0008, 0x3938: 0x0008, 0x3939: 0x0008, 0x393a: 0x0008, 0x393b: 0x0008, + 0x393c: 0x0008, 0x393d: 0x0008, 0x393e: 0x0008, 0x393f: 0x0008, + // Block 0xe5, offset 0x3940 + 0x394c: 0x0008, 0x394d: 0x0008, 0x394e: 0x0008, 0x394f: 0x0008, 0x3950: 0x0008, 0x3951: 0x0008, + 0x3952: 0x0008, 0x3953: 0x0008, 0x3954: 0x0008, 0x3955: 0x0008, 0x3956: 0x0008, 0x3957: 0x0008, + 0x3958: 0x0008, 0x3959: 0x0008, 0x395a: 0x0008, 0x395b: 0x0008, 0x395c: 0x0008, 0x395d: 0x0008, + 0x395e: 0x0008, 0x395f: 0x0008, 0x3960: 0x0008, 0x3961: 0x0008, 0x3962: 0x0008, 0x3963: 0x0008, + 0x3964: 0x0008, 0x3965: 0x0008, 0x3966: 0x0008, 0x3967: 0x0008, 0x3968: 0x0008, 0x3969: 0x0008, + 0x396a: 0x0008, 0x396b: 0x0008, 0x396c: 0x0008, 0x396d: 0x0008, 0x396e: 0x0008, 0x396f: 0x0008, + 0x3970: 0x0008, 0x3971: 0x0008, 0x3972: 0x0008, 0x3973: 0x0008, 0x3974: 0x0008, 0x3975: 0x0008, + 0x3976: 0x0008, 0x3977: 0x0008, 0x3978: 0x0008, 0x3979: 0x0008, 0x397a: 0x0008, + 0x397c: 0x0008, 0x397d: 0x0008, 0x397e: 0x0008, 0x397f: 0x0008, + // Block 0xe6, offset 0x3980 + 0x3980: 0x0008, 0x3981: 0x0008, 0x3982: 0x0008, 0x3983: 0x0008, 0x3984: 0x0008, 0x3985: 0x0008, + 0x3987: 0x0008, 0x3988: 0x0008, 0x3989: 0x0008, 0x398a: 0x0008, 0x398b: 0x0008, + 0x398c: 0x0008, 0x398d: 0x0008, 0x398e: 0x0008, 0x398f: 0x0008, 0x3990: 0x0008, 0x3991: 0x0008, + 0x3992: 0x0008, 0x3993: 0x0008, 0x3994: 0x0008, 0x3995: 0x0008, 0x3996: 0x0008, 0x3997: 0x0008, + 0x3998: 0x0008, 0x3999: 0x0008, 0x399a: 0x0008, 0x399b: 0x0008, 0x399c: 0x0008, 0x399d: 0x0008, + 0x399e: 0x0008, 0x399f: 0x0008, 0x39a0: 0x0008, 0x39a1: 0x0008, 0x39a2: 0x0008, 0x39a3: 0x0008, + 0x39a4: 0x0008, 0x39a5: 0x0008, 0x39a6: 0x0008, 0x39a7: 0x0008, 0x39a8: 0x0008, 0x39a9: 0x0008, + 0x39aa: 0x0008, 0x39ab: 0x0008, 0x39ac: 0x0008, 0x39ad: 0x0008, 0x39ae: 0x0008, 0x39af: 0x0008, + 0x39b0: 0x0008, 0x39b1: 0x0008, 0x39b2: 0x0008, 0x39b3: 0x0008, 0x39b4: 0x0008, 0x39b5: 0x0008, + 0x39b6: 0x0008, 0x39b7: 0x0008, 0x39b8: 0x0008, 0x39b9: 0x0008, 0x39ba: 0x0008, 0x39bb: 0x0008, + 0x39bc: 0x0008, 0x39bd: 0x0008, 0x39be: 0x0008, 0x39bf: 0x0008, + // Block 0xe7, offset 0x39c0 + 0x39d8: 0x0008, 0x39d9: 0x0008, 0x39da: 0x0008, 0x39db: 0x0008, 0x39dc: 0x0008, 0x39dd: 0x0008, + 0x39de: 0x0008, 0x39df: 0x0008, + 0x39ee: 0x0008, 0x39ef: 0x0008, + 0x39f0: 0x0008, 0x39f1: 0x0008, 0x39f2: 0x0008, 0x39f3: 0x0008, 0x39f4: 0x0008, 0x39f5: 0x0008, + 0x39f6: 0x0008, 0x39f7: 0x0008, 0x39f8: 0x0008, 0x39f9: 0x0008, 0x39fa: 0x0008, 0x39fb: 0x0008, + 0x39fc: 0x0008, 0x39fd: 0x0008, 0x39fe: 0x0008, 0x39ff: 0x0008, + // Block 0xe8, offset 0x3a00 + 0x3a00: 0x0002, 0x3a01: 0x0002, 0x3a02: 0x0002, 0x3a03: 0x0002, 0x3a04: 0x0002, 0x3a05: 0x0002, + 0x3a06: 0x0002, 0x3a07: 0x0002, 0x3a08: 0x0002, 0x3a09: 0x0002, 0x3a0a: 0x0002, 0x3a0b: 0x0002, + 0x3a0c: 0x0002, 0x3a0d: 0x0002, 0x3a0e: 0x0002, 0x3a0f: 0x0002, 0x3a10: 0x0002, 0x3a11: 0x0002, + 0x3a12: 0x0002, 0x3a13: 0x0002, 0x3a14: 0x0002, 0x3a15: 0x0002, 0x3a16: 0x0002, 0x3a17: 0x0002, + 0x3a18: 0x0002, 0x3a19: 0x0002, 0x3a1a: 0x0002, 0x3a1b: 0x0002, 0x3a1c: 0x0002, 0x3a1d: 0x0002, + 0x3a1e: 0x0002, 0x3a1f: 0x0002, 0x3a20: 0x0024, 0x3a21: 0x0024, 0x3a22: 0x0024, 0x3a23: 0x0024, + 0x3a24: 0x0024, 0x3a25: 0x0024, 0x3a26: 0x0024, 0x3a27: 0x0024, 0x3a28: 0x0024, 0x3a29: 0x0024, + 0x3a2a: 0x0024, 0x3a2b: 0x0024, 0x3a2c: 0x0024, 0x3a2d: 0x0024, 0x3a2e: 0x0024, 0x3a2f: 0x0024, + 0x3a30: 0x0024, 0x3a31: 0x0024, 0x3a32: 0x0024, 0x3a33: 0x0024, 0x3a34: 0x0024, 0x3a35: 0x0024, + 0x3a36: 0x0024, 0x3a37: 0x0024, 0x3a38: 0x0024, 0x3a39: 0x0024, 0x3a3a: 0x0024, 0x3a3b: 0x0024, + 0x3a3c: 0x0024, 0x3a3d: 0x0024, 0x3a3e: 0x0024, 0x3a3f: 0x0024, + // Block 0xe9, offset 0x3a40 + 0x3a40: 0x0002, 0x3a41: 0x0002, 0x3a42: 0x0002, 0x3a43: 0x0002, 0x3a44: 0x0002, 0x3a45: 0x0002, + 0x3a46: 0x0002, 0x3a47: 0x0002, 0x3a48: 0x0002, 0x3a49: 0x0002, 0x3a4a: 0x0002, 0x3a4b: 0x0002, + 0x3a4c: 0x0002, 0x3a4d: 0x0002, 0x3a4e: 0x0002, 0x3a4f: 0x0002, 0x3a50: 0x0002, 0x3a51: 0x0002, + 0x3a52: 0x0002, 0x3a53: 0x0002, 0x3a54: 0x0002, 0x3a55: 0x0002, 0x3a56: 0x0002, 0x3a57: 0x0002, + 0x3a58: 0x0002, 0x3a59: 0x0002, 0x3a5a: 0x0002, 0x3a5b: 0x0002, 0x3a5c: 0x0002, 0x3a5d: 0x0002, + 0x3a5e: 0x0002, 0x3a5f: 0x0002, 0x3a60: 0x0002, 0x3a61: 0x0002, 0x3a62: 0x0002, 0x3a63: 0x0002, + 0x3a64: 0x0002, 0x3a65: 0x0002, 0x3a66: 0x0002, 0x3a67: 0x0002, 0x3a68: 0x0002, 0x3a69: 0x0002, + 0x3a6a: 0x0002, 0x3a6b: 0x0002, 0x3a6c: 0x0002, 0x3a6d: 0x0002, 0x3a6e: 0x0002, 0x3a6f: 0x0002, + 0x3a70: 0x0002, 0x3a71: 0x0002, 0x3a72: 0x0002, 0x3a73: 0x0002, 0x3a74: 0x0002, 0x3a75: 0x0002, + 0x3a76: 0x0002, 0x3a77: 0x0002, 0x3a78: 0x0002, 0x3a79: 0x0002, 0x3a7a: 0x0002, 0x3a7b: 0x0002, + 0x3a7c: 0x0002, 0x3a7d: 0x0002, 0x3a7e: 0x0002, 0x3a7f: 0x0002, + // Block 0xea, offset 0x3a80 + 0x3a80: 0x0024, 0x3a81: 0x0024, 0x3a82: 0x0024, 0x3a83: 0x0024, 0x3a84: 0x0024, 0x3a85: 0x0024, + 0x3a86: 0x0024, 0x3a87: 0x0024, 0x3a88: 0x0024, 0x3a89: 0x0024, 0x3a8a: 0x0024, 0x3a8b: 0x0024, + 0x3a8c: 0x0024, 0x3a8d: 0x0024, 0x3a8e: 0x0024, 0x3a8f: 0x0024, 0x3a90: 0x0024, 0x3a91: 0x0024, + 0x3a92: 0x0024, 0x3a93: 0x0024, 0x3a94: 0x0024, 0x3a95: 0x0024, 0x3a96: 0x0024, 0x3a97: 0x0024, + 0x3a98: 0x0024, 0x3a99: 0x0024, 0x3a9a: 0x0024, 0x3a9b: 0x0024, 0x3a9c: 0x0024, 0x3a9d: 0x0024, + 0x3a9e: 0x0024, 0x3a9f: 0x0024, 0x3aa0: 0x0024, 0x3aa1: 0x0024, 0x3aa2: 0x0024, 0x3aa3: 0x0024, + 0x3aa4: 0x0024, 0x3aa5: 0x0024, 0x3aa6: 0x0024, 0x3aa7: 0x0024, 0x3aa8: 0x0024, 0x3aa9: 0x0024, + 0x3aaa: 0x0024, 0x3aab: 0x0024, 0x3aac: 0x0024, 0x3aad: 0x0024, 0x3aae: 0x0024, 0x3aaf: 0x0024, + 0x3ab0: 0x0002, 0x3ab1: 0x0002, 0x3ab2: 0x0002, 0x3ab3: 0x0002, 0x3ab4: 0x0002, 0x3ab5: 0x0002, + 0x3ab6: 0x0002, 0x3ab7: 0x0002, 0x3ab8: 0x0002, 0x3ab9: 0x0002, 0x3aba: 0x0002, 0x3abb: 0x0002, + 0x3abc: 0x0002, 0x3abd: 0x0002, 0x3abe: 0x0002, 0x3abf: 0x0002, } // graphemesIndex: 25 blocks, 1600 entries, 1600 bytes @@ -1297,113 +1602,116 @@ var graphemesIndex = [1600]property{ 0xf0: 0x14, 0xf3: 0x16, // Block 0x4, offset 0x100 0x120: 0x0e, 0x121: 0x0f, 0x122: 0x10, 0x123: 0x11, 0x124: 0x12, 0x125: 0x13, 0x126: 0x14, 0x127: 0x15, - 0x128: 0x16, 0x129: 0x17, 0x12a: 0x16, 0x12b: 0x18, 0x12c: 0x19, 0x12d: 0x1a, 0x12e: 0x1b, 0x12f: 0x1c, - 0x130: 0x1d, 0x131: 0x1e, 0x132: 0x1f, 0x133: 0x20, 0x134: 0x21, 0x135: 0x22, 0x136: 0x23, 0x137: 0x24, - 0x138: 0x25, 0x139: 0x26, 0x13a: 0x27, 0x13b: 0x28, 0x13c: 0x29, 0x13d: 0x2a, 0x13e: 0x2b, 0x13f: 0x2c, + 0x128: 0x16, 0x129: 0x17, 0x12a: 0x18, 0x12b: 0x19, 0x12c: 0x1a, 0x12d: 0x1b, 0x12e: 0x1c, 0x12f: 0x1d, + 0x130: 0x1e, 0x131: 0x1f, 0x132: 0x20, 0x133: 0x21, 0x134: 0x22, 0x135: 0x23, 0x136: 0x24, 0x137: 0x25, + 0x138: 0x26, 0x139: 0x27, 0x13a: 0x28, 0x13b: 0x29, 0x13c: 0x2a, 0x13d: 0x2b, 0x13e: 0x2c, 0x13f: 0x2d, // Block 0x5, offset 0x140 - 0x140: 0x2d, 0x141: 0x2e, 0x142: 0x2f, 0x144: 0x30, 0x145: 0x31, 0x146: 0x32, 0x147: 0x33, - 0x14d: 0x34, - 0x15c: 0x35, 0x15d: 0x36, 0x15e: 0x37, 0x15f: 0x38, - 0x160: 0x39, 0x162: 0x3a, 0x164: 0x3b, - 0x168: 0x3c, 0x169: 0x3d, 0x16a: 0x3e, 0x16b: 0x3f, 0x16c: 0x40, 0x16d: 0x41, 0x16e: 0x42, 0x16f: 0x43, - 0x170: 0x44, 0x173: 0x45, 0x177: 0x02, + 0x140: 0x2e, 0x141: 0x2f, 0x142: 0x30, 0x144: 0x31, 0x145: 0x32, 0x146: 0x33, 0x147: 0x34, + 0x14d: 0x35, + 0x15c: 0x36, 0x15d: 0x37, 0x15e: 0x38, 0x15f: 0x39, + 0x160: 0x3a, 0x162: 0x3b, 0x164: 0x3c, + 0x168: 0x3d, 0x169: 0x3e, 0x16a: 0x3f, 0x16b: 0x40, 0x16c: 0x41, 0x16d: 0x42, 0x16e: 0x43, 0x16f: 0x44, + 0x170: 0x45, 0x173: 0x46, 0x177: 0x02, // Block 0x6, offset 0x180 - 0x180: 0x46, 0x181: 0x47, 0x183: 0x48, 0x184: 0x49, 0x186: 0x4a, - 0x18c: 0x4b, 0x18e: 0x4c, 0x18f: 0x4d, + 0x180: 0x47, 0x181: 0x48, 0x183: 0x49, 0x184: 0x4a, 0x186: 0x4b, + 0x18c: 0x4c, 0x18f: 0x4d, 0x193: 0x4e, 0x196: 0x4f, 0x197: 0x50, - 0x198: 0x51, 0x199: 0x52, 0x19a: 0x53, 0x19b: 0x52, 0x19c: 0x54, 0x19d: 0x55, 0x19e: 0x56, - 0x1a4: 0x57, - 0x1ac: 0x58, 0x1ad: 0x59, - 0x1b3: 0x5a, 0x1b5: 0x5b, 0x1b7: 0x5c, + 0x198: 0x51, 0x199: 0x52, 0x19a: 0x53, 0x19b: 0x54, 0x19c: 0x55, 0x19d: 0x56, 0x19e: 0x57, + 0x1a4: 0x58, + 0x1ac: 0x59, 0x1ad: 0x5a, + 0x1b3: 0x5b, 0x1b5: 0x5c, 0x1b7: 0x5d, // Block 0x7, offset 0x1c0 - 0x1c0: 0x5d, 0x1c2: 0x5e, - 0x1ca: 0x5f, + 0x1c0: 0x5e, 0x1c2: 0x5f, + 0x1ca: 0x60, // Block 0x8, offset 0x200 - 0x219: 0x60, 0x21a: 0x61, 0x21b: 0x62, - 0x220: 0x63, 0x222: 0x64, 0x223: 0x65, 0x224: 0x66, 0x225: 0x67, 0x226: 0x68, 0x227: 0x69, - 0x228: 0x6a, 0x229: 0x6b, 0x22a: 0x6c, 0x22b: 0x6d, 0x22f: 0x6e, - 0x230: 0x6f, 0x231: 0x70, 0x232: 0x71, 0x233: 0x72, 0x234: 0x73, 0x235: 0x74, 0x236: 0x75, 0x237: 0x6f, - 0x238: 0x70, 0x239: 0x71, 0x23a: 0x72, 0x23b: 0x73, 0x23c: 0x74, 0x23d: 0x75, 0x23e: 0x6f, 0x23f: 0x70, + 0x219: 0x61, 0x21a: 0x62, 0x21b: 0x63, + 0x220: 0x64, 0x222: 0x65, 0x223: 0x66, 0x224: 0x67, 0x225: 0x68, 0x226: 0x69, 0x227: 0x6a, + 0x228: 0x6b, 0x229: 0x6c, 0x22a: 0x6d, 0x22b: 0x6e, 0x22f: 0x6f, + 0x230: 0x70, 0x231: 0x71, 0x232: 0x72, 0x233: 0x73, 0x234: 0x74, 0x235: 0x75, 0x236: 0x76, 0x237: 0x70, + 0x238: 0x71, 0x239: 0x72, 0x23a: 0x73, 0x23b: 0x74, 0x23c: 0x75, 0x23d: 0x76, 0x23e: 0x70, 0x23f: 0x71, // Block 0x9, offset 0x240 - 0x240: 0x71, 0x241: 0x72, 0x242: 0x73, 0x243: 0x74, 0x244: 0x75, 0x245: 0x6f, 0x246: 0x70, 0x247: 0x71, - 0x248: 0x72, 0x249: 0x73, 0x24a: 0x74, 0x24b: 0x75, 0x24c: 0x6f, 0x24d: 0x70, 0x24e: 0x71, 0x24f: 0x72, - 0x250: 0x73, 0x251: 0x74, 0x252: 0x75, 0x253: 0x6f, 0x254: 0x70, 0x255: 0x71, 0x256: 0x72, 0x257: 0x73, - 0x258: 0x74, 0x259: 0x75, 0x25a: 0x6f, 0x25b: 0x70, 0x25c: 0x71, 0x25d: 0x72, 0x25e: 0x73, 0x25f: 0x74, - 0x260: 0x75, 0x261: 0x6f, 0x262: 0x70, 0x263: 0x71, 0x264: 0x72, 0x265: 0x73, 0x266: 0x74, 0x267: 0x75, - 0x268: 0x6f, 0x269: 0x70, 0x26a: 0x71, 0x26b: 0x72, 0x26c: 0x73, 0x26d: 0x74, 0x26e: 0x75, 0x26f: 0x6f, - 0x270: 0x70, 0x271: 0x71, 0x272: 0x72, 0x273: 0x73, 0x274: 0x74, 0x275: 0x75, 0x276: 0x6f, 0x277: 0x70, - 0x278: 0x71, 0x279: 0x72, 0x27a: 0x73, 0x27b: 0x74, 0x27c: 0x75, 0x27d: 0x6f, 0x27e: 0x70, 0x27f: 0x71, + 0x240: 0x72, 0x241: 0x73, 0x242: 0x74, 0x243: 0x75, 0x244: 0x76, 0x245: 0x70, 0x246: 0x71, 0x247: 0x72, + 0x248: 0x73, 0x249: 0x74, 0x24a: 0x75, 0x24b: 0x76, 0x24c: 0x70, 0x24d: 0x71, 0x24e: 0x72, 0x24f: 0x73, + 0x250: 0x74, 0x251: 0x75, 0x252: 0x76, 0x253: 0x70, 0x254: 0x71, 0x255: 0x72, 0x256: 0x73, 0x257: 0x74, + 0x258: 0x75, 0x259: 0x76, 0x25a: 0x70, 0x25b: 0x71, 0x25c: 0x72, 0x25d: 0x73, 0x25e: 0x74, 0x25f: 0x75, + 0x260: 0x76, 0x261: 0x70, 0x262: 0x71, 0x263: 0x72, 0x264: 0x73, 0x265: 0x74, 0x266: 0x75, 0x267: 0x76, + 0x268: 0x70, 0x269: 0x71, 0x26a: 0x72, 0x26b: 0x73, 0x26c: 0x74, 0x26d: 0x75, 0x26e: 0x76, 0x26f: 0x70, + 0x270: 0x71, 0x271: 0x72, 0x272: 0x73, 0x273: 0x74, 0x274: 0x75, 0x275: 0x76, 0x276: 0x70, 0x277: 0x71, + 0x278: 0x72, 0x279: 0x73, 0x27a: 0x74, 0x27b: 0x75, 0x27c: 0x76, 0x27d: 0x70, 0x27e: 0x71, 0x27f: 0x72, // Block 0xa, offset 0x280 - 0x280: 0x72, 0x281: 0x73, 0x282: 0x74, 0x283: 0x75, 0x284: 0x6f, 0x285: 0x70, 0x286: 0x71, 0x287: 0x72, - 0x288: 0x73, 0x289: 0x74, 0x28a: 0x75, 0x28b: 0x6f, 0x28c: 0x70, 0x28d: 0x71, 0x28e: 0x72, 0x28f: 0x73, - 0x290: 0x74, 0x291: 0x75, 0x292: 0x6f, 0x293: 0x70, 0x294: 0x71, 0x295: 0x72, 0x296: 0x73, 0x297: 0x74, - 0x298: 0x75, 0x299: 0x6f, 0x29a: 0x70, 0x29b: 0x71, 0x29c: 0x72, 0x29d: 0x73, 0x29e: 0x74, 0x29f: 0x75, - 0x2a0: 0x6f, 0x2a1: 0x70, 0x2a2: 0x71, 0x2a3: 0x72, 0x2a4: 0x73, 0x2a5: 0x74, 0x2a6: 0x75, 0x2a7: 0x6f, - 0x2a8: 0x70, 0x2a9: 0x71, 0x2aa: 0x72, 0x2ab: 0x73, 0x2ac: 0x74, 0x2ad: 0x75, 0x2ae: 0x6f, 0x2af: 0x70, - 0x2b0: 0x71, 0x2b1: 0x72, 0x2b2: 0x73, 0x2b3: 0x74, 0x2b4: 0x75, 0x2b5: 0x6f, 0x2b6: 0x70, 0x2b7: 0x71, - 0x2b8: 0x72, 0x2b9: 0x73, 0x2ba: 0x74, 0x2bb: 0x75, 0x2bc: 0x6f, 0x2bd: 0x70, 0x2be: 0x71, 0x2bf: 0x72, + 0x280: 0x73, 0x281: 0x74, 0x282: 0x75, 0x283: 0x76, 0x284: 0x70, 0x285: 0x71, 0x286: 0x72, 0x287: 0x73, + 0x288: 0x74, 0x289: 0x75, 0x28a: 0x76, 0x28b: 0x70, 0x28c: 0x71, 0x28d: 0x72, 0x28e: 0x73, 0x28f: 0x74, + 0x290: 0x75, 0x291: 0x76, 0x292: 0x70, 0x293: 0x71, 0x294: 0x72, 0x295: 0x73, 0x296: 0x74, 0x297: 0x75, + 0x298: 0x76, 0x299: 0x70, 0x29a: 0x71, 0x29b: 0x72, 0x29c: 0x73, 0x29d: 0x74, 0x29e: 0x75, 0x29f: 0x76, + 0x2a0: 0x70, 0x2a1: 0x71, 0x2a2: 0x72, 0x2a3: 0x73, 0x2a4: 0x74, 0x2a5: 0x75, 0x2a6: 0x76, 0x2a7: 0x70, + 0x2a8: 0x71, 0x2a9: 0x72, 0x2aa: 0x73, 0x2ab: 0x74, 0x2ac: 0x75, 0x2ad: 0x76, 0x2ae: 0x70, 0x2af: 0x71, + 0x2b0: 0x72, 0x2b1: 0x73, 0x2b2: 0x74, 0x2b3: 0x75, 0x2b4: 0x76, 0x2b5: 0x70, 0x2b6: 0x71, 0x2b7: 0x72, + 0x2b8: 0x73, 0x2b9: 0x74, 0x2ba: 0x75, 0x2bb: 0x76, 0x2bc: 0x70, 0x2bd: 0x71, 0x2be: 0x72, 0x2bf: 0x73, // Block 0xb, offset 0x2c0 - 0x2c0: 0x73, 0x2c1: 0x74, 0x2c2: 0x75, 0x2c3: 0x6f, 0x2c4: 0x70, 0x2c5: 0x71, 0x2c6: 0x72, 0x2c7: 0x73, - 0x2c8: 0x74, 0x2c9: 0x75, 0x2ca: 0x6f, 0x2cb: 0x70, 0x2cc: 0x71, 0x2cd: 0x72, 0x2ce: 0x73, 0x2cf: 0x74, - 0x2d0: 0x75, 0x2d1: 0x6f, 0x2d2: 0x70, 0x2d3: 0x71, 0x2d4: 0x72, 0x2d5: 0x73, 0x2d6: 0x74, 0x2d7: 0x75, - 0x2d8: 0x6f, 0x2d9: 0x70, 0x2da: 0x71, 0x2db: 0x72, 0x2dc: 0x73, 0x2dd: 0x74, 0x2de: 0x76, 0x2df: 0x77, + 0x2c0: 0x74, 0x2c1: 0x75, 0x2c2: 0x76, 0x2c3: 0x70, 0x2c4: 0x71, 0x2c5: 0x72, 0x2c6: 0x73, 0x2c7: 0x74, + 0x2c8: 0x75, 0x2c9: 0x76, 0x2ca: 0x70, 0x2cb: 0x71, 0x2cc: 0x72, 0x2cd: 0x73, 0x2ce: 0x74, 0x2cf: 0x75, + 0x2d0: 0x76, 0x2d1: 0x70, 0x2d2: 0x71, 0x2d3: 0x72, 0x2d4: 0x73, 0x2d5: 0x74, 0x2d6: 0x75, 0x2d7: 0x76, + 0x2d8: 0x70, 0x2d9: 0x71, 0x2da: 0x72, 0x2db: 0x73, 0x2dc: 0x74, 0x2dd: 0x75, 0x2de: 0x77, 0x2df: 0x78, // Block 0xc, offset 0x300 - 0x32c: 0x78, - 0x338: 0x79, 0x33b: 0x7a, 0x33e: 0x61, 0x33f: 0x7b, + 0x32c: 0x79, + 0x338: 0x7a, 0x33b: 0x7b, 0x33e: 0x62, 0x33f: 0x7c, // Block 0xd, offset 0x340 - 0x347: 0x7c, - 0x34b: 0x7d, 0x34d: 0x7e, - 0x368: 0x7f, 0x36b: 0x80, - 0x374: 0x81, - 0x37a: 0x82, 0x37b: 0x83, 0x37d: 0x84, 0x37e: 0x85, + 0x347: 0x7d, + 0x34b: 0x7e, 0x34d: 0x7f, + 0x368: 0x80, 0x36b: 0x81, + 0x374: 0x82, 0x375: 0x83, + 0x37a: 0x84, 0x37b: 0x85, 0x37d: 0x86, 0x37e: 0x87, // Block 0xe, offset 0x380 - 0x380: 0x86, 0x381: 0x87, 0x382: 0x88, 0x383: 0x89, 0x384: 0x8a, 0x385: 0x8b, 0x386: 0x8c, 0x387: 0x8d, - 0x388: 0x8e, 0x389: 0x8f, 0x38b: 0x90, 0x38c: 0x21, 0x38d: 0x91, - 0x390: 0x92, 0x391: 0x93, 0x392: 0x94, 0x393: 0x95, 0x396: 0x96, 0x397: 0x97, - 0x398: 0x98, 0x399: 0x99, 0x39a: 0x9a, 0x39c: 0x9b, - 0x3a0: 0x9c, 0x3a4: 0x9d, 0x3a5: 0x9e, 0x3a7: 0x9f, - 0x3a8: 0xa0, 0x3a9: 0xa1, 0x3aa: 0xa2, - 0x3b0: 0xa3, 0x3b2: 0xa4, 0x3b4: 0xa5, 0x3b5: 0xa6, 0x3b6: 0xa7, - 0x3bb: 0xa8, 0x3bc: 0xa9, 0x3bd: 0xaa, + 0x380: 0x88, 0x381: 0x89, 0x382: 0x8a, 0x383: 0x8b, 0x384: 0x8c, 0x385: 0x8d, 0x386: 0x8e, 0x387: 0x8f, + 0x388: 0x90, 0x389: 0x91, 0x38b: 0x92, 0x38c: 0x93, 0x38d: 0x94, 0x38e: 0x95, 0x38f: 0x96, + 0x390: 0x97, 0x391: 0x98, 0x392: 0x99, 0x393: 0x9a, 0x396: 0x9b, 0x397: 0x9c, + 0x398: 0x9d, 0x399: 0x9e, 0x39a: 0x9f, 0x39c: 0xa0, + 0x3a0: 0xa1, 0x3a4: 0xa2, 0x3a5: 0xa3, 0x3a7: 0xa4, + 0x3a8: 0xa5, 0x3a9: 0xa6, 0x3aa: 0xa7, 0x3ad: 0xa8, + 0x3b0: 0xa9, 0x3b2: 0xaa, 0x3b4: 0xab, 0x3b5: 0xac, 0x3b6: 0xad, + 0x3bb: 0xae, 0x3bc: 0xaf, 0x3bd: 0xb0, // Block 0xf, offset 0x3c0 - 0x3d0: 0xab, 0x3d1: 0xac, + 0x3d0: 0xb1, 0x3d1: 0xb2, // Block 0x10, offset 0x400 - 0x42b: 0xad, 0x42c: 0xae, - 0x43d: 0xaf, 0x43e: 0xb0, 0x43f: 0xb1, + 0x404: 0xb3, + 0x42b: 0xb4, 0x42c: 0xb5, + 0x435: 0xb6, + 0x43d: 0xb7, 0x43e: 0xb8, 0x43f: 0xb9, // Block 0x11, offset 0x440 - 0x472: 0xb2, + 0x472: 0xba, // Block 0x12, offset 0x480 - 0x4bc: 0xb3, 0x4bd: 0xb4, + 0x4bc: 0xbb, 0x4bd: 0xbc, // Block 0x13, offset 0x4c0 - 0x4c5: 0xb5, 0x4c6: 0xb6, - 0x4c9: 0xb7, - 0x4e8: 0xb8, 0x4e9: 0xb9, 0x4ea: 0xba, + 0x4c5: 0xbd, 0x4c6: 0xbe, + 0x4c9: 0xbf, + 0x4e8: 0xc0, 0x4e9: 0xc1, 0x4ea: 0xc2, // Block 0x14, offset 0x500 - 0x500: 0xbb, 0x502: 0xbc, 0x504: 0xae, - 0x50a: 0xbd, 0x50b: 0xbe, - 0x513: 0xbe, - 0x523: 0xbf, 0x525: 0xc0, + 0x500: 0xc3, 0x502: 0xc4, 0x504: 0xb5, + 0x50a: 0xc5, 0x50b: 0xc6, + 0x513: 0xc6, 0x517: 0xc7, + 0x51b: 0xc8, + 0x523: 0xc9, 0x525: 0xca, // Block 0x15, offset 0x540 - 0x540: 0x52, 0x541: 0x52, 0x542: 0x52, 0x543: 0x52, 0x544: 0xc1, 0x545: 0xc2, 0x546: 0xc3, 0x547: 0xc4, - 0x548: 0xc5, 0x549: 0xc6, 0x54a: 0x52, 0x54b: 0x52, 0x54c: 0x52, 0x54d: 0x52, 0x54e: 0x52, 0x54f: 0xc7, - 0x550: 0x52, 0x551: 0x52, 0x552: 0x52, 0x553: 0x52, 0x554: 0xc8, 0x555: 0xc9, 0x556: 0x52, 0x557: 0x52, - 0x558: 0x52, 0x559: 0xca, 0x55a: 0x52, 0x55b: 0x52, 0x55d: 0xcb, 0x55f: 0xcc, - 0x560: 0xcd, 0x561: 0xce, 0x562: 0xcf, 0x563: 0x52, 0x564: 0xd0, 0x565: 0xd1, 0x566: 0x52, 0x567: 0x52, - 0x568: 0x52, 0x569: 0x52, 0x56a: 0x52, 0x56b: 0x52, - 0x570: 0x52, 0x571: 0x52, 0x572: 0x52, 0x573: 0x52, 0x574: 0x52, 0x575: 0x52, 0x576: 0x52, 0x577: 0x52, - 0x578: 0x52, 0x579: 0x52, 0x57a: 0x52, 0x57b: 0x52, 0x57c: 0x52, 0x57d: 0x52, 0x57e: 0x52, 0x57f: 0xc8, + 0x540: 0xcb, 0x542: 0xcc, 0x543: 0xcd, 0x545: 0xce, 0x546: 0xcf, 0x547: 0xd0, + 0x548: 0xd1, 0x549: 0xd2, 0x54a: 0xd3, 0x54b: 0xd3, 0x54c: 0xd4, 0x54d: 0xd3, 0x54e: 0xd5, 0x54f: 0xd6, + 0x550: 0xd3, 0x551: 0xd3, 0x552: 0xd3, 0x553: 0xd7, 0x554: 0xd8, 0x555: 0xd9, 0x556: 0xda, 0x557: 0xdb, + 0x558: 0xd3, 0x559: 0xdc, 0x55a: 0xd3, 0x55b: 0xdd, 0x55f: 0xde, + 0x560: 0xdf, 0x561: 0xe0, 0x562: 0xe1, 0x563: 0xe2, 0x564: 0xe3, 0x565: 0xe4, 0x566: 0xd3, 0x567: 0xd3, + 0x569: 0xe5, 0x56a: 0xd3, 0x56b: 0xd3, + 0x570: 0xd3, 0x571: 0xd3, 0x572: 0xd3, 0x573: 0xd3, 0x574: 0xd3, 0x575: 0xd3, 0x576: 0xd3, 0x577: 0xd3, + 0x578: 0xd3, 0x579: 0xd3, 0x57a: 0xd3, 0x57b: 0xd3, 0x57c: 0xd3, 0x57d: 0xd3, 0x57e: 0xd3, 0x57f: 0xd8, // Block 0x16, offset 0x580 0x590: 0x0b, 0x591: 0x0c, 0x593: 0x0d, 0x596: 0x0e, 0x59b: 0x0f, 0x59c: 0x10, 0x59d: 0x11, 0x59e: 0x12, 0x59f: 0x13, // Block 0x17, offset 0x5c0 - 0x5c0: 0xd2, 0x5c1: 0x02, 0x5c2: 0xd3, 0x5c3: 0xd3, 0x5c4: 0x02, 0x5c5: 0x02, 0x5c6: 0x02, 0x5c7: 0xd4, - 0x5c8: 0xd3, 0x5c9: 0xd3, 0x5ca: 0xd3, 0x5cb: 0xd3, 0x5cc: 0xd3, 0x5cd: 0xd3, 0x5ce: 0xd3, 0x5cf: 0xd3, - 0x5d0: 0xd3, 0x5d1: 0xd3, 0x5d2: 0xd3, 0x5d3: 0xd3, 0x5d4: 0xd3, 0x5d5: 0xd3, 0x5d6: 0xd3, 0x5d7: 0xd3, - 0x5d8: 0xd3, 0x5d9: 0xd3, 0x5da: 0xd3, 0x5db: 0xd3, 0x5dc: 0xd3, 0x5dd: 0xd3, 0x5de: 0xd3, 0x5df: 0xd3, - 0x5e0: 0xd3, 0x5e1: 0xd3, 0x5e2: 0xd3, 0x5e3: 0xd3, 0x5e4: 0xd3, 0x5e5: 0xd3, 0x5e6: 0xd3, 0x5e7: 0xd3, - 0x5e8: 0xd3, 0x5e9: 0xd3, 0x5ea: 0xd3, 0x5eb: 0xd3, 0x5ec: 0xd3, 0x5ed: 0xd3, 0x5ee: 0xd3, 0x5ef: 0xd3, - 0x5f0: 0xd3, 0x5f1: 0xd3, 0x5f2: 0xd3, 0x5f3: 0xd3, 0x5f4: 0xd3, 0x5f5: 0xd3, 0x5f6: 0xd3, 0x5f7: 0xd3, - 0x5f8: 0xd3, 0x5f9: 0xd3, 0x5fa: 0xd3, 0x5fb: 0xd3, 0x5fc: 0xd3, 0x5fd: 0xd3, 0x5fe: 0xd3, 0x5ff: 0xd3, + 0x5c0: 0xe6, 0x5c1: 0x02, 0x5c2: 0xe7, 0x5c3: 0xe7, 0x5c4: 0x02, 0x5c5: 0x02, 0x5c6: 0x02, 0x5c7: 0xe8, + 0x5c8: 0xe7, 0x5c9: 0xe7, 0x5ca: 0xe7, 0x5cb: 0xe7, 0x5cc: 0xe7, 0x5cd: 0xe7, 0x5ce: 0xe7, 0x5cf: 0xe7, + 0x5d0: 0xe7, 0x5d1: 0xe7, 0x5d2: 0xe7, 0x5d3: 0xe7, 0x5d4: 0xe7, 0x5d5: 0xe7, 0x5d6: 0xe7, 0x5d7: 0xe7, + 0x5d8: 0xe7, 0x5d9: 0xe7, 0x5da: 0xe7, 0x5db: 0xe7, 0x5dc: 0xe7, 0x5dd: 0xe7, 0x5de: 0xe7, 0x5df: 0xe7, + 0x5e0: 0xe7, 0x5e1: 0xe7, 0x5e2: 0xe7, 0x5e3: 0xe7, 0x5e4: 0xe7, 0x5e5: 0xe7, 0x5e6: 0xe7, 0x5e7: 0xe7, + 0x5e8: 0xe7, 0x5e9: 0xe7, 0x5ea: 0xe7, 0x5eb: 0xe7, 0x5ec: 0xe7, 0x5ed: 0xe7, 0x5ee: 0xe7, 0x5ef: 0xe7, + 0x5f0: 0xe7, 0x5f1: 0xe7, 0x5f2: 0xe7, 0x5f3: 0xe7, 0x5f4: 0xe7, 0x5f5: 0xe7, 0x5f6: 0xe7, 0x5f7: 0xe7, + 0x5f8: 0xe7, 0x5f9: 0xe7, 0x5fa: 0xe7, 0x5fb: 0xe7, 0x5fc: 0xe7, 0x5fd: 0xe7, 0x5fe: 0xe7, 0x5ff: 0xe7, // Block 0x18, offset 0x600 0x620: 0x15, } diff --git a/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go b/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go deleted file mode 100644 index e213486380..0000000000 --- a/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go +++ /dev/null @@ -1,100 +0,0 @@ -package iterators - -import "github.com/clipperhouse/stringish" - -type SplitFunc[T stringish.Interface] func(T, bool) (int, T, error) - -// Iterator is a generic iterator for words that are either []byte or string. -// Iterate while Next() is true, and access the word via Value(). -type Iterator[T stringish.Interface] struct { - split SplitFunc[T] - data T - start int - pos int -} - -// New creates a new Iterator for the given data and SplitFunc. -func New[T stringish.Interface](split SplitFunc[T], data T) *Iterator[T] { - return &Iterator[T]{ - split: split, - data: data, - } -} - -// SetText sets the text for the iterator to operate on, and resets all state. -func (iter *Iterator[T]) SetText(data T) { - iter.data = data - iter.start = 0 - iter.pos = 0 -} - -// Split sets the SplitFunc for the Iterator. -func (iter *Iterator[T]) Split(split SplitFunc[T]) { - iter.split = split -} - -// Next advances the iterator to the next token. It returns false when there -// are no remaining tokens or an error occurred. -func (iter *Iterator[T]) Next() bool { - if iter.pos == len(iter.data) { - return false - } - if iter.pos > len(iter.data) { - panic("SplitFunc advanced beyond the end of the data") - } - - iter.start = iter.pos - - advance, _, err := iter.split(iter.data[iter.pos:], true) - if err != nil { - panic(err) - } - if advance <= 0 { - panic("SplitFunc returned a zero or negative advance") - } - - iter.pos += advance - if iter.pos > len(iter.data) { - panic("SplitFunc advanced beyond the end of the data") - } - - return true -} - -// Value returns the current token. -func (iter *Iterator[T]) Value() T { - return iter.data[iter.start:iter.pos] -} - -// Start returns the byte position of the current token in the original data. -func (iter *Iterator[T]) Start() int { - return iter.start -} - -// End returns the byte position after the current token in the original data. -func (iter *Iterator[T]) End() int { - return iter.pos -} - -// Reset resets the iterator to the beginning of the data. -func (iter *Iterator[T]) Reset() { - iter.start = 0 - iter.pos = 0 -} - -func (iter *Iterator[T]) First() T { - if len(iter.data) == 0 { - return iter.data - } - advance, _, err := iter.split(iter.data, true) - if err != nil { - panic(err) - } - if advance <= 0 { - panic("SplitFunc returned a zero or negative advance") - } - if advance > len(iter.data) { - panic("SplitFunc advanced beyond the end of the data") - } - return iter.data[:advance] -} diff --git a/vendor/github.com/olekukonko/errors/chain.go b/vendor/github.com/olekukonko/errors/chain.go index 5dc73a5852..2bb390ef00 100644 --- a/vendor/github.com/olekukonko/errors/chain.go +++ b/vendor/github.com/olekukonko/errors/chain.go @@ -527,46 +527,34 @@ func (c *Chain) wrapCallable(fn interface{}, args ...interface{}) (func() error, } // executeStep runs a single step, applying retries if configured. +// This version is synchronous and avoids the bugs caused by the previous goroutine-based implementation. func (c *Chain) executeStep(ctx context.Context, step *chainStep) error { + // First, check if the context has already been canceled before starting the step. + // This allows the chain to fail fast. select { case <-ctx.Done(): return ctx.Err() default: + // Context is still active, proceed. } + + // If the step has retry logic configured... if step.config.retry != nil { - retry := step.config.retry.Transform(WithContext(ctx)) - // Wrap step execution to respect context - wrappedFn := func() error { - type result struct { - err error - } - done := make(chan result, 1) - go func() { - done <- result{err: step.execute()} - }() - select { - case res := <-done: - return res.err - case <-ctx.Done(): - return ctx.Err() - } - } - return retry.Execute(wrappedFn) - } - // Non-retry case also respects context - type result struct { - err error - } - done := make(chan result, 1) - go func() { - done <- result{err: step.execute()} - }() - select { - case res := <-done: - return res.err - case <-ctx.Done(): - return ctx.Err() + // Create a new retry instance that is aware of the chain's context. + // The retry executor will be responsible for checking ctx.Done() between attempts. + retryExecutor := step.config.retry.Transform(WithContext(ctx)) + + // Execute the step's function directly. The retry mechanism will manage the loop, + // delays, and context cancellation checks. We pass step.execute without any + // extra goroutine wrappers. + return retryExecutor.Execute(step.execute) } + + // For a simple, non-retrying step, execute the function directly and synchronously + // in the current goroutine. This is the simplest, fastest, and most correct approach. + // It ensures that database connections are used and returned to the pool sequentially, + // preventing the deadlock issue. + return step.execute() } // enhanceError wraps an error with additional context from the step. diff --git a/vendor/github.com/olekukonko/errors/errors.go b/vendor/github.com/olekukonko/errors/errors.go index 4f6509da98..6bf3bca6f8 100644 --- a/vendor/github.com/olekukonko/errors/errors.go +++ b/vendor/github.com/olekukonko/errors/errors.go @@ -95,6 +95,10 @@ type contextItem struct { // context, cause, and metadata like code and category. It is thread-safe and // supports pooling for performance. type Error struct { + // Fields used in atomic operations. Place them at the beginning of the + // struct to ensure proper alignment across all architectures. + count uint64 // Occurrence count for tracking frequency. + // Primary fields (frequently accessed). msg string // The error message displayed by Error(). name string // The error name or type (e.g., "AuthError"). @@ -103,7 +107,6 @@ type Error struct { // Secondary metadata. template string // Fallback message template if msg is empty. category string // Error category (e.g., "network"). - count uint64 // Occurrence count for tracking frequency. code int32 // HTTP-like status code (e.g., 400, 500). smallCount int32 // Number of items in smallContext. @@ -172,7 +175,7 @@ func newError() *Error { // // err := errors.Empty().With("key", "value").WithCode(400) func Empty() *Error { - return emptyError + return newError() } // Named creates an error with the specified name and captures a stack trace. @@ -213,10 +216,18 @@ func New(text string) *Error { // err := errors.Newf("query failed: %w", cause) // // err.Error() will match fmt.Errorf("query failed: %w", cause).Error() // // errors.Unwrap(err) == cause -func Newf(format string, args ...interface{}) *Error { +func Newf(f any, args ...interface{}) *Error { + var format string + switch v := f.(type) { + case string: + format = v + case fmt.Stringer: + format = v.String() + default: + panic("Newf: format must be a string or fmt.Stringer") + } err := newError() - // --- Start: Parsing and Validation (mostly unchanged) --- var wCount int var wArgPos = -1 var wArg error @@ -356,11 +367,10 @@ func Newf(format string, args ...interface{}) *Error { err.formatWrapped = false return err } - // --- End: Parsing and Validation --- - // --- Start: Processing Valid Format String --- + // Start: Processing Valid Format String if wCount == 1 && wArg != nil { - // --- Handle %w: Simulate for Sprintf and pre-format --- + // Handle %w: Simulate for Sprintf and pre-format err.cause = wArg // Set the cause for unwrapping err.formatWrapped = true // Signal that msg is the final formatted string @@ -397,10 +407,10 @@ func Newf(format string, args ...interface{}) *Error { // Store the final, fully formatted string, matching fmt.Errorf output err.msg = result } - // --- End %w Simulation --- + // End %w Simulation } else { - // --- No %w or wArg is nil: Format directly (original logic) --- + // No %w or wArg is nil: Format directly (original logic) result, fmtErr := FmtErrorCheck(format, args...) if fmtErr != nil { err.msg = fmt.Sprintf("errors.Newf: formatting error for format %q: %v", format, fmtErr) @@ -411,7 +421,7 @@ func Newf(format string, args ...interface{}) *Error { err.formatWrapped = false // Ensure false if no %w was involved } } - // --- End: Processing Valid Format String --- + // End: Processing Valid Format String return err } @@ -448,38 +458,6 @@ func FmtErrorCheck(format string, args ...interface{}) (result string, err error return result, nil } -// countFmtArgs counts format specifiers that consume arguments in a format string. -// Ignores %% and non-consuming verbs like %n. -// Internal use by Newf for argument validation. -func countFmtArgs(format string) int { - count := 0 - runes := []rune(format) - i := 0 - for i < len(runes) { - if runes[i] == '%' { - if i+1 < len(runes) && runes[i+1] == '%' { - i += 2 // Skip %% - continue - } - i++ // Move past % - for i < len(runes) && (runes[i] == '+' || runes[i] == '-' || runes[i] == '#' || - runes[i] == ' ' || runes[i] == '0' || - (runes[i] >= '1' && runes[i] <= '9') || runes[i] == '.') { - i++ - } - if i < len(runes) { - if strings.ContainsRune("vTtbcdoqxXUeEfFgGsp", runes[i]) { - count++ - } - i++ // Move past verb - } - } else { - i++ - } - } - return count -} - // Std creates a standard error using errors.New for compatibility. // Does not capture stack traces or add context. // Example: @@ -700,8 +678,8 @@ func (e *Error) Error() string { return e.msg // Return the pre-formatted fmt.Errorf-compatible string } - // --- Original logic for errors not created via Newf("%w", ...) --- - // --- or errors created via New/Named and then Wrap() called. --- + // Original logic for errors not created via Newf("%w", ...) + // or errors created via New/Named and then Wrap() called. var buf strings.Builder // Append primary message part (msg, template, or name) diff --git a/vendor/github.com/olekukonko/errors/helper.go b/vendor/github.com/olekukonko/errors/helper.go index 06c2adc55c..2f4af34e94 100644 --- a/vendor/github.com/olekukonko/errors/helper.go +++ b/vendor/github.com/olekukonko/errors/helper.go @@ -430,3 +430,8 @@ func Wrapf(err error, format string, args ...interface{}) *Error { e.cause = err return e } + +// Err creates a new Error with the given message and wraps the provided error as its cause. +func Err(msg string, err error) *Error { + return New(msg).Wrap(err) +} diff --git a/vendor/github.com/olekukonko/ll/.goreleaser.yaml b/vendor/github.com/olekukonko/ll/.goreleaser.yaml new file mode 100644 index 0000000000..937f363415 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/.goreleaser.yaml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +version: 2 + +project_name: ll + +# For a library repo, publish source archives instead of binaries. +source: + enabled: true + name_template: "{{ .ProjectName }}_{{ .Version }}" + + # Optional: include/exclude files in the source archive (defaults are usually fine) + # files: + # - README.md + # - LICENSE + # - go.mod + # - go.sum + # - "**/*.go" + +# No binaries to build. +builds: [] + +## Other Information + +checksum: + name_template: "checksums.txt" + +snapshot: + version_template: "{{ .Tag }}-next" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^chore:" + - "^ci:" diff --git a/vendor/github.com/olekukonko/ll/Makefile b/vendor/github.com/olekukonko/ll/Makefile new file mode 100644 index 0000000000..e9bf75d34f --- /dev/null +++ b/vendor/github.com/olekukonko/ll/Makefile @@ -0,0 +1,99 @@ +# Git remote for pushing tags +REMOTE ?= origin + +# Version for release tagging (required for tag/release targets) +RELEASE_VERSION ?= + +# Convenience +GO ?= go +GOLANGCI ?= golangci-lint +GORELEASER?= goreleaser + +.PHONY: help \ + test race bench fmt tidy lint check \ + ensure-clean ensure-release-version tag tag-delete \ + release release-dry + +help: + @echo "Targets:" + @echo " fmt - gofmt + go fmt" + @echo " tidy - go mod tidy" + @echo " test - go test ./..." + @echo " race - go test -race ./..." + @echo " bench - go test -bench=. ./..." + @echo " lint - golangci-lint run ./... (if installed)" + @echo " check - fmt + tidy + test + race" + @echo "" + @echo "Release targets:" + @echo " tag - Create annotated tag RELEASE_VERSION and push" + @echo " tag-delete - Delete tag RELEASE_VERSION locally + remote" + @echo " release - tag + goreleaser release --clean (if you use goreleaser)" + @echo " release-dry - tag + goreleaser release --clean --skip=publish" + @echo "" + @echo "Usage:" + @echo " make check" + @echo " make tag RELEASE_VERSION=v0.1.2" + @echo " make release RELEASE_VERSION=v0.1.2" + +fmt: + @echo "Formatting..." + gofmt -w -s . + $(GO) fmt ./... + +tidy: + @echo "Tidying..." + $(GO) mod tidy + +test: + @echo "Testing..." + $(GO) test ./... -count=1 + +race: + @echo "Race testing..." + $(GO) test ./... -race -count=1 + +bench: + @echo "Bench..." + $(GO) test ./... -bench=. -run=^$$ + +lint: + @echo "Linting..." + @command -v $(GOLANGCI) >/dev/null 2>&1 || { echo "golangci-lint not found"; exit 1; } + $(GOLANGCI) run ./... + +check: fmt tidy test race + +# -------------------------- +# Release helpers +# -------------------------- + +ensure-clean: + @echo "Checking git working tree..." + @git diff --quiet || (echo "Error: tracked changes exist. Commit/stash them."; exit 1) + @test -z "$$(git status --porcelain)" || (echo "Error: uncommitted/untracked files:"; git status --porcelain; exit 1) + @echo "OK: working tree clean" + +ensure-release-version: + @test -n "$(RELEASE_VERSION)" || (echo "Error: set RELEASE_VERSION, e.g. make tag RELEASE_VERSION=v0.1.2"; exit 1) + +tag: ensure-clean ensure-release-version + @if git rev-parse "$(RELEASE_VERSION)" >/dev/null 2>&1; then \ + echo "Error: tag $(RELEASE_VERSION) already exists. Bump version."; \ + exit 1; \ + fi + @echo "Tagging $(RELEASE_VERSION) at HEAD $$(git rev-parse --short HEAD)" + @git tag -a $(RELEASE_VERSION) -m "$(RELEASE_VERSION)" + @git push $(REMOTE) $(RELEASE_VERSION) + +tag-delete: ensure-release-version + @echo "Deleting tag $(RELEASE_VERSION) locally + remote..." + @git tag -d $(RELEASE_VERSION) 2>/dev/null || true + @git push $(REMOTE) :refs/tags/$(RELEASE_VERSION) || true + +release: tag + @command -v $(GORELEASER) >/dev/null 2>&1 || { echo "goreleaser not found"; exit 1; } + $(GORELEASER) release --clean + +release-dry: tag + @command -v $(GORELEASER) >/dev/null 2>&1 || { echo "goreleaser not found"; exit 1; } + $(GORELEASER) release --clean --skip=publish diff --git a/vendor/github.com/olekukonko/ll/README.md b/vendor/github.com/olekukonko/ll/README.md index facb4736ca..aaa58a4b8f 100644 --- a/vendor/github.com/olekukonko/ll/README.md +++ b/vendor/github.com/olekukonko/ll/README.md @@ -1,17 +1,19 @@ # ll - A Modern Structured Logging Library for Go -`ll` is a high-performance, production-ready logging library for Go, designed to provide **hierarchical namespaces**, **structured logging**, **middleware pipelines**, **conditional logging**, and support for multiple output formats, including text, JSON, colorized logs, and compatibility with Go’s `slog`. It’s ideal for applications requiring fine-grained log control, extensibility, and scalability. +`ll` is a high-performance, production-ready logging library for Go, designed to provide **hierarchical namespaces**, **structured logging**, **middleware pipelines**, **conditional logging**, and support for multiple output formats, including text, JSON, colorized logs, syslog, VictoriaLogs, and compatibility with Go's `slog`. It's ideal for applications requiring fine-grained log control, extensibility, and scalability. ## Key Features -- **Hierarchical Namespaces**: Organize logs with fine-grained control over subsystems (e.g., "app/db"). -- **Structured Logging**: Add key-value metadata for machine-readable logs. -- **Middleware Pipeline**: Customize log processing with error-based rejection. -- **Conditional Logging**: Optimize performance by skipping unnecessary log operations. -- **Multiple Output Formats**: Support for text, JSON, colorized logs, and `slog` integration. -- **Debugging Utilities**: Inspect variables (`Dbg`), binary data (`Dump`), and stack traces (`Stack`). -- **Thread-Safe**: Built for concurrent use with mutex-protected state. -- **Performance Optimized**: Minimal allocations and efficient namespace caching. +- **Logging Enabled by Default** - Zero configuration to start logging +- **Hierarchical Namespaces** - Organize logs with fine-grained control over subsystems (e.g., "app/db") +- **Structured Logging** - Add key-value metadata for machine-readable logs +- **Middleware Pipeline** - Customize log processing with rate limiting, sampling, and deduplication +- **Conditional & Error-Based Logging** - Optimize performance with fluent `If`, `IfErr`, `IfAny`, `IfOne` chains +- **Multiple Output Formats** - Text, JSON, colorized ANSI, syslog, VictoriaLogs, and `slog` integration +- **Advanced Debugging Utilities** - Source-aware `Dbg()`, hex/ASCII `Dump()`, private field `Inspect()`, and stack traces +- **Production Ready** - Buffered batching, log rotation, duplicate suppression, and rate limiting +- **Thread-Safe** - Built for high-concurrency with atomic operations, sharded mutexes, and lock-free fast paths +- **Performance Optimized** - Zero allocations for disabled logs, sync.Pool buffers, LRU caching for source files ## Installation @@ -21,235 +23,267 @@ Install `ll` using Go modules: go get github.com/olekukonko/ll ``` -Ensure you have Go 1.21 or later for optimal compatibility. - -## Getting Started - -Here’s a quick example to start logging with `ll`: +Requires Go 1.21 or later. +## Quick Start ```go package main -import ( - "github.com/olekukonko/ll" -) +import "github.com/olekukonko/ll" func main() { - // Create a logger with namespace "app" - logger := ll.New("") - - // enable output - logger.Enable() - - // Basic log - logger.Info("Welcome") // Output: [app] INFO: Application started - - logger = logger.Namespace("app") - - // Basic log - logger.Info("start at :8080") // Output: [app] INFO: Application started - - //Output - //INFO: Welcome - //[app] INFO: start at :8080 -} - -``` - -```go -package main - -import ( - "github.com/olekukonko/ll" - "github.com/olekukonko/ll/lh" - "os" -) - -func main() { - // Chaining - logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout)) - - // Basic log - logger.Info("Application started") // Output: [app] INFO: Application started - - // Structured log with fields - logger.Fields("user", "alice", "status", 200).Info("User logged in") - // Output: [app] INFO: User logged in [user=alice status=200] - - // Conditional log - debugMode := false - logger.If(debugMode).Debug("Debug info") // No output (debugMode is false) + // Logger is ENABLED by default - no .Enable() needed! + logger := ll.New("app") + + // Basic logging - works immediately + logger.Info("Server starting") // Output: [app] INFO: Server starting + logger.Warn("Memory high") // Output: [app] WARN: Memory high + logger.Error("Connection failed") // Output: [app] ERROR: Connection failed + + // Structured fields + logger.Fields("user", "alice", "status", 200).Info("Login successful") + // Output: [app] INFO: Login successful [user=alice status=200] } ``` -## Core Features +**That's it. No `.Enable()`, no handlers to configure—it just works.** -### 1. Hierarchical Namespaces +## Core Concepts -Namespaces allow you to organize logs hierarchically, enabling precise control over logging for different parts of your application. This is especially useful for large systems with multiple components. +### 1. Enabled by Default, Configurable When Needed -**Benefits**: -- **Granular Control**: Enable/disable logs for specific subsystems (e.g., "app/db" vs. "app/api"). -- **Scalability**: Manage log volume in complex applications. -- **Readability**: Clear namespace paths improve traceability. +Unlike many logging libraries that require explicit enabling, `ll` **logs immediately**. This eliminates boilerplate and reduces the chance of missing logs in production. -**Example**: ```go -logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout)) +// This works out of the box: +ll.Info("Service started") // Output: [] INFO: Service started -// Child loggers -dbLogger := logger.Namespace("db") -apiLogger := logger.Namespace("api").Style(lx.NestedPath) - -// Namespace control -logger.NamespaceEnable("app/db") // Enable DB logs -logger.NamespaceDisable("app/api") // Disable API logs - -dbLogger.Info("Query executed") // Output: [app/db] INFO: Query executed -apiLogger.Info("Request received") // No output +// But you still have full control: +ll.Disable() // Global shutdown +ll.Enable() // Reactivate ``` -### 2. Structured Logging +### 2. Hierarchical Namespaces -Add key-value metadata to logs for machine-readable output, making it easier to query and analyze logs in tools like ELK or Grafana. +Organize logs hierarchically with precise control over subsystems: -**Example**: ```go -logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout)) +// Create a logger hierarchy +root := ll.New("app") +db := root.Namespace("database") +cache := root.Namespace("cache").Style(lx.NestedPath) -// Variadic fields -logger.Fields("user", "bob", "status", 200).Info("Request completed") -// Output: [app] INFO: Request completed [user=bob status=200] +// Control logging per namespace +root.NamespaceEnable("app/database") // Enable database logs +root.NamespaceDisable("app/cache") // Disable cache logs + +db.Info("Connected") // Output: [app/database] INFO: Connected +cache.Info("Hit") // No output (disabled) +``` + +### 3. Structured Logging with Ordered Fields + +Fields maintain insertion order and support fluent chaining: + +```go +// Fluent key-value pairs +logger. + Fields("request_id", "req-123"). + Fields("user", "alice"). + Fields("duration_ms", 42). + Info("Request processed") // Map-based fields -logger.Field(map[string]interface{}{"method": "GET"}).Info("Request") -// Output: [app] INFO: Request [method=GET] +logger.Field(map[string]interface{}{ + "method": "POST", + "path": "/api/users", +}).Debug("API call") + +// Persistent context (included in ALL subsequent logs) +logger.AddContext("environment", "production", "version", "1.2.3") +logger.Info("Deployed") // Output: ... [environment=production version=1.2.3] ``` -### 3. Middleware Pipeline +### 4. Conditional & Error-Based Logging -Customize log processing with a middleware pipeline. Middleware functions can enrich, filter, or transform logs, using an error-based rejection mechanism (non-nil errors stop logging). +Optimize performance with fluent conditional chains that **completely skip processing** when conditions are false: -**Example**: ```go -logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout)) +// Boolean conditions +logger.If(debugMode).Debug("Detailed diagnostics") // No overhead when false +logger.If(featureEnabled).Info("Feature used") -// Enrich logs with app metadata -logger.Use(ll.FuncMiddleware(func(e *lx.Entry) error { - if e.Fields == nil { - e.Fields = make(map[string]interface{}) - } - e.Fields["app"] = "myapp" - return nil -})) +// Error conditions +err := db.Query() +logger.IfErr(err).Error("Query failed") // Logs only if err != nil -// Filter low-level logs -logger.Use(ll.FuncMiddleware(func(e *lx.Entry) error { - if e.Level < lx.LevelWarn { - return fmt.Errorf("level too low") +// Multiple conditions - ANY true +logger.IfErrAny(err1, err2, err3).Fatal("System failure") + +// Multiple conditions - ALL true +logger.IfErrOne(validateErr, authErr).Error("Both checks failed") + +// Chain conditions +logger. + If(debugMode). + IfErr(queryErr). + Fields("query", sql). + Debug("Query debug") +``` + +**Performance**: When conditions are false, the logger returns immediately with zero allocations. + +### 5. Powerful Debugging Toolkit + +`ll` includes advanced debugging utilities not found in standard logging libraries: + +#### Dbg() - Source-Aware Variable Inspection +Captures both variable name AND value from your source code: + +```go +x := 42 +user := &User{Name: "Alice"} +ll.Dbg(x, user) +// Output: [file.go:123] x = 42, *user = &{Name:Alice} +``` + +#### Dump() - Hex/ASCII Binary Inspection +Perfect for protocol debugging and binary data: + +```go +ll.Handler(lh.NewColorizedHandler(os.Stdout)) +ll.Dump([]byte("hello\nworld")) +// Output: Colorized hex/ASCII dump with offset markers +``` + +#### Inspect() - Private Field Reflection +Reveals unexported fields, embedded structs, and pointer internals: + +```go +type secret struct { + password string // unexported! +} + +s := secret{password: "hunter2"} +ll.Inspect(s) +// Output: [file.go:123] INSPECT: { +// "(password)": "hunter2" // Note the parentheses +// } +``` + +#### Stack() - Configurable Stack Traces +```go +ll.StackSize(8192) // Larger buffer for deep stacks +ll.Stack("Critical failure") +// Output: ERROR: Critical failure [stack=goroutine 1 [running]...] +``` + +#### Mark() - Execution Flow Tracing +```go +func process() { + ll.Mark() // *MARK*: [file.go:123] + ll.Mark("phase1") // *phase1*: [file.go:124] + // ... work ... +} +``` + +### 6. Production-Ready Handlers + +```go +import ( + "github.com/olekukonko/ll" + "github.com/olekukonko/ll/lh" + "github.com/olekukonko/ll/l3rd/syslog" + "github.com/olekukonko/ll/l3rd/victoria" +) + +// JSON for structured logging +logger.Handler(lh.NewJSONHandler(os.Stdout)) + +// Colorized for development +logger.Handler(lh.NewColorizedHandler(os.Stdout, + lh.WithColorTheme("dark"), + lh.WithColorIntensity(lh.IntensityVibrant), +)) + +// Buffered for high throughput (100 entries or 10 seconds) +buffered := lh.NewBuffered( + lh.NewJSONHandler(os.Stdout), + lh.WithBatchSize(100), + lh.WithFlushInterval(10 * time.Second), +) +logger.Handler(buffered) +defer buffered.Close() // Ensures flush on exit + +// Syslog integration +syslogHandler, _ := syslog.New( + syslog.WithTag("myapp"), + syslog.WithFacility(syslog.LOG_LOCAL0), +) +logger.Handler(syslogHandler) + +// VictoriaLogs (cloud-native) +victoriaHandler, _ := victoria.New( + victoria.WithURL("http://victoria-logs:9428"), + victoria.WithAppName("payment-service"), + victoria.WithEnvironment("production"), + victoria.WithBatching(200, 5*time.Second), +) +logger.Handler(victoriaHandler) +``` + +### 7. Middleware Pipeline + +Transform, filter, or reject logs with a middleware pipeline: + +```go +// Rate limiting - 10 logs per second maximum +rateLimiter := lm.NewRateLimiter(lx.LevelInfo, 10, time.Second) +logger.Use(rateLimiter) + +// Sampling - 10% of debug logs +sampler := lm.NewSampling(lx.LevelDebug, 0.1) +logger.Use(sampler) + +// Deduplication - suppress identical logs for 2 seconds +deduper := lh.NewDedup(logger.GetHandler(), 2*time.Second) +logger.Handler(deduper) + +// Custom middleware +logger.Use(ll.Middle(func(e *lx.Entry) error { + if strings.Contains(e.Message, "password") { + return fmt.Errorf("sensitive information redacted") } return nil })) - -logger.Info("Ignored") // No output (filtered) -logger.Warn("Warning") // Output: [app] WARN: Warning [app=myapp] ``` -### 4. Conditional Logging +### 8. Global Convenience API -Optimize performance by skipping expensive log operations when conditions are false, ideal for production environments. +Use package-level functions for quick logging without creating loggers: -**Example**: ```go -logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout)) +import "github.com/olekukonko/ll" -featureEnabled := true -logger.If(featureEnabled).Fields("action", "update").Info("Feature used") -// Output: [app] INFO: Feature used [action=update] - -logger.If(false).Info("Ignored") // No output, no processing +func main() { + ll.Info("Server starting") // Global logger + ll.Fields("port", 8080).Info("Listening") + + // Conditional logging at package level + ll.If(simulation).Debug("Test mode") + ll.IfErr(err).Error("Startup failed") + + // Debug utilities + ll.Dbg(config) + ll.Dump(requestBody) + ll.Inspect(complexStruct) +} ``` -### 5. Multiple Output Formats +## Real-World Examples -`ll` supports various output formats, including human-readable text, colorized logs, JSON, and integration with Go’s `slog` package. - -**Example**: -```go -logger := ll.New("app").Enable() - -// Text output -logger.Handler(lh.NewTextHandler(os.Stdout)) -logger.Info("Text log") // Output: [app] INFO: Text log - -// JSON output -logger.Handler(lh.NewJSONHandler(os.Stdout, time.RFC3339Nano)) -logger.Info("JSON log") // Output: {"timestamp":"...","level":"INFO","message":"JSON log","namespace":"app"} - -// Slog integration -slogText := slog.NewTextHandler(os.Stdout, nil) -logger.Handler(lh.NewSlogHandler(slogText)) -logger.Info("Slog log") // Output: level=INFO msg="Slog log" namespace=app class=Text -``` - -### 6. Debugging Utilities - -`ll` provides powerful tools for debugging, including variable inspection, binary data dumps, and stack traces. - -#### Core Debugging Methods - -1. **Dbg - Contextual Inspection** - Inspects variables with file and line context, preserving variable names and handling all Go types. - ```go - x := 42 - user := struct{ Name string }{"Alice"} - ll.Dbg(x) // Output: [file.go:123] x = 42 - ll.Dbg(user) // Output: [file.go:124] user = [Name:Alice] - ``` - -2. **Dump - Binary Inspection** - Displays a hex/ASCII view of data, optimized for strings, bytes, and complex types (with JSON fallback). - ```go - ll.Handler(lh.NewColorizedHandler(os.Stdout)) - ll.Dump("hello\nworld") // Output: Hex/ASCII dump (see example/dump.png) - ``` - -3. **Stack - Stack Inspection** - Logs a stack trace for debugging critical errors. - ```go - ll.Handler(lh.NewColorizedHandler(os.Stdout)) - ll.Stack("Critical error") // Output: [app] ERROR: Critical error [stack=...] (see example/stack.png) - ``` - -4**General Output** - Logs a output in structured way for inspection of public & private values. - ```go - ll.Handler(lh.NewColorizedHandler(os.Stdout)) - ll.Output(&SomeStructWithPrivateValues{}) - ``` - -#### Performance Tracking -Measure execution time for performance analysis. -```go -// Automatic measurement -defer ll.Measure(func() { time.Sleep(time.Millisecond) })() -// Output: [app] INFO: function executed [duration=~1ms] - -// Explicit benchmarking -start := time.Now() -time.Sleep(time.Millisecond) -ll.Benchmark(start) // Output: [app] INFO: benchmark [start=... end=... duration=...] -``` - -**Performance Notes**: -- `Dbg` calls are disabled at compile-time when not enabled. -- `Dump` optimizes for primitive types, strings, and bytes with zero-copy paths. -- Stack traces are configurable via `StackSize`. - -## Real-World Example: Web Server - -A practical example of using `ll` in a web server with structured logging, middleware, and `slog` integration: +### Web Server with Structured Logging ```go package main @@ -257,110 +291,127 @@ package main import ( "github.com/olekukonko/ll" "github.com/olekukonko/ll/lh" - "log/slog" "net/http" - "os" "time" ) func main() { - // Initialize logger with slog handler - slogHandler := slog.NewJSONHandler(os.Stdout, nil) - logger := ll.New("server").Enable().Handler(lh.NewSlogHandler(slogHandler)) - - // HTTP child logger - httpLogger := logger.Namespace("http").Style(lx.NestedPath) - - // Middleware for request ID - httpLogger.Use(ll.FuncMiddleware(func(e *lx.Entry) error { - if e.Fields == nil { - e.Fields = make(map[string]interface{}) - } - e.Fields["request_id"] = "req-" + time.Now().String() - return nil - })) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // Root logger - enabled by default + log := ll.New("server") + + // JSON output for production + log.Handler(lh.NewJSONHandler(os.Stdout)) + + // Request logger with context + http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { + reqLog := log.Namespace("http").Fields( + "method", r.Method, + "path", r.URL.Path, + "request_id", r.Header.Get("X-Request-ID"), + ) + start := time.Now() - httpLogger.Fields("method", r.Method, "path", r.URL.Path).Info("Request received") - w.Write([]byte("Hello, world!")) - httpLogger.Fields("duration_ms", time.Since(start).Milliseconds()).Info("Request completed") + reqLog.Info("request started") + + // ... handle request ... + + reqLog.Fields( + "status", 200, + "duration_ms", time.Since(start).Milliseconds(), + ).Info("request completed") }) - - logger.Info("Starting server on :8080") + + log.Info("Server listening on :8080") http.ListenAndServe(":8080", nil) } ``` -**Sample Output (JSON via slog)**: -```json -{"level":"INFO","msg":"Starting server on :8080","namespace":"server"} -{"level":"INFO","msg":"Request received","namespace":"server/http","class":"Text","method":"GET","path":"/","request_id":"req-..."} -{"level":"INFO","msg":"Request completed","namespace":"server/http","class":"Text","duration_ms":1,"request_id":"req-..."} +### Microservice with VictoriaLogs + +```go +package main + +import ( + "github.com/olekukonko/ll" + "github.com/olekukonko/ll/l3rd/victoria" +) + +func main() { + // Production setup + vlHandler, _ := victoria.New( + victoria.WithURL("http://logs.internal:9428"), + victoria.WithAppName("payment-api"), + victoria.WithEnvironment("production"), + victoria.WithVersion("1.2.3"), + victoria.WithBatching(500, 2*time.Second), + victoria.WithRetry(3), + ) + defer vlHandler.Close() + + logger := ll.New("payment"). + Handler(vlHandler). + AddContext("region", "us-east-1") + + logger.Info("Payment service initialized") + + // Conditional error handling + if err := processPayment(); err != nil { + logger.IfErr(err). + Fields("payment_id", paymentID). + Error("Payment processing failed") + } +} ``` +## Performance + +`ll` is engineered for high-performance environments: + +| Operation | Time/op | Allocations | +|-----------|---------|-------------| +| **Disabled log** | **15.9 ns** | **0 allocs** | +| Simple text log | 176 ns | 2 allocs | +| With 2 fields | 383 ns | 4 allocs | +| JSON output | 1006 ns | 13 allocs | +| Namespace lookup (cached) | 550 ns | 6 allocs | +| Deduplication | 214 ns | 2 allocs | + +**Key optimizations**: +- Zero allocations when logs are skipped (conditional, disabled) +- Atomic operations for hot paths +- Sync.Pool for buffer reuse +- LRU cache for source file lines (Dbg) +- Sharded mutexes for deduplication + ## Why Choose `ll`? -- **Granular Control**: Hierarchical namespaces for precise log management. -- **Performance**: Conditional logging and optimized concatenation reduce overhead. -- **Extensibility**: Middleware pipeline for custom log processing. -- **Structured Output**: Machine-readable logs with key-value metadata. -- **Flexible Formats**: Text, JSON, colorized, and `slog` support. -- **Debugging Power**: Advanced tools like `Dbg`, `Dump`, and `Stack` for deep inspection. -- **Thread-Safe**: Safe for concurrent use in high-throughput applications. +| Feature | `ll` | `slog` | `zap` | `logrus` | +|---------|------|--------|-------|----------| +| **Enabled by default** | ✅ | ❌ | ❌ | ❌ | +| Hierarchical namespaces | ✅ | ❌ | ❌ | ❌ | +| Conditional logging | ✅ | ❌ | ❌ | ❌ | +| Error-based conditions | ✅ | ❌ | ❌ | ❌ | +| Source-aware Dbg() | ✅ | ❌ | ❌ | ❌ | +| Private field inspection | ✅ | ❌ | ❌ | ❌ | +| Hex/ASCII Dump() | ✅ | ❌ | ❌ | ❌ | +| Middleware pipeline | ✅ | ❌ | ✅ (limited) | ❌ | +| Deduplication | ✅ | ❌ | ❌ | ❌ | +| Rate limiting | ✅ | ❌ | ❌ | ❌ | +| VictoriaLogs support | ✅ | ❌ | ❌ | ❌ | +| Syslog support | ✅ | ❌ | ❌ | ✅ | +| Zero-allocs disabled logs | ✅ | ❌ | ❌ | ❌ | +| Thread-safe | ✅ | ✅ | ✅ | ✅ | -## Comparison with Other Libraries +## Documentation -| Feature | `ll` | `log` (stdlib) | `slog` (stdlib) | `zap` | -|--------------------------|--------------------------|----------------|-----------------|-------------------| -| Hierarchical Namespaces | ✅ | ❌ | ❌ | ❌ | -| Structured Logging | ✅ (Fields, Context) | ❌ | ✅ | ✅ | -| Middleware Pipeline | ✅ | ❌ | ❌ | ✅ (limited) | -| Conditional Logging | ✅ (If, IfOne, IfAny) | ❌ | ❌ | ❌ | -| Slog Compatibility | ✅ | ❌ | ✅ (native) | ❌ | -| Debugging (Dbg, Dump) | ✅ | ❌ | ❌ | ❌ | -| Performance (disabled logs) | High (conditional) | Low | Medium | High | -| Output Formats | Text, JSON, Color, Slog | Text | Text, JSON | JSON, Text | - -## Benchmarks - -`ll` is optimized for performance, particularly for disabled logs and structured logging: -- **Disabled Logs**: 30% faster than `slog` due to efficient conditional checks. -- **Structured Logging**: 2x faster than `log` with minimal allocations. -- **Namespace Caching**: Reduces overhead for hierarchical lookups. - -See `ll_bench_test.go` for detailed benchmarks on namespace creation, cloning, and field building. - -## Testing and Stability - -The `ll` library includes a comprehensive test suite (`ll_test.go`) covering: -- Logger configuration, namespaces, and conditional logging. -- Middleware, rate limiting, and sampling. -- Handler output formats (text, JSON, slog). -- Debugging utilities (`Dbg`, `Dump`, `Stack`). - -Recent improvements: -- Fixed sampling middleware for reliable behavior at edge cases (0.0 and 1.0 rates). -- Enhanced documentation across `conditional.go`, `field.go`, `global.go`, `ll.go`, `lx.go`, and `ns.go`. -- Added `slog` compatibility via `lh.SlogHandler`. +- [GoDoc](https://pkg.go.dev/github.com/olekukonko/ll) - Full API documentation +- [Examples](_example/) - Runable example code +- [Benchmarks](tests/ll_bench_test.go) - Performance benchmarks ## Contributing -Contributions are welcome! To contribute: -1. Fork the repository: `github.com/olekukonko/ll`. -2. Create a feature branch: `git checkout -b feature/your-feature`. -3. Commit changes: `git commit -m "Add your feature"`. -4. Push to the branch: `git push origin feature/your-feature`. -5. Open a pull request with a clear description. - -Please include tests in `ll_test.go` and update documentation as needed. Follow the Go coding style and run `go test ./...` before submitting. +Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. ## License -`ll` is licensed under the MIT License. See [LICENSE](LICENSE) for details. - -## Resources - -- **Source Code**: [github.com/olekukonko/ll](https://github.com/olekukonko/ll) -- **Issue Tracker**: [github.com/olekukonko/ll/issues](https://github.com/olekukonko/ll/issues) -- **GoDoc**: [pkg.go.dev/github.com/olekukonko/ll](https://pkg.go.dev/github.com/olekukonko/ll) \ No newline at end of file +MIT License - see [LICENSE](LICENSE) for details. \ No newline at end of file diff --git a/vendor/github.com/olekukonko/ll/comb.hcl b/vendor/github.com/olekukonko/ll/comb.hcl new file mode 100644 index 0000000000..791ed93bba --- /dev/null +++ b/vendor/github.com/olekukonko/ll/comb.hcl @@ -0,0 +1,7 @@ +recursive = true +output_file = "all.txt" +extensions = [".go"] +exclude_dirs = ["_examples", "_lab", "_tmp", "pkg", "lab","bin","dist","assets","oppor"] +exclude_files = [""] +use_gitignore = true +detailed = true \ No newline at end of file diff --git a/vendor/github.com/olekukonko/ll/conditional.go b/vendor/github.com/olekukonko/ll/conditional.go index 0ec9e4b842..7aa4434f38 100644 --- a/vendor/github.com/olekukonko/ll/conditional.go +++ b/vendor/github.com/olekukonko/ll/conditional.go @@ -12,7 +12,7 @@ type Conditional struct { // If creates a conditional logger that logs only if the condition is true. // It returns a Conditional struct that wraps the logger, enabling conditional logging methods. // This method is typically called on a Logger instance to start a conditional chain. -// Thread-safe via the underlying logger’s mutex. +// Thread-safe via the underlying logger's mutex. // Example: // // logger := New("app").Enable() @@ -22,27 +22,6 @@ func (l *Logger) If(condition bool) *Conditional { return &Conditional{logger: l, condition: condition} } -// IfOne creates a conditional logger that logs only if all conditions are true. -// It evaluates a variadic list of boolean conditions, setting the condition to true only if -// all are true (logical AND). Returns a new Conditional with the result. Thread-safe via the -// underlying logger. -// Example: -// -// logger := New("app").Enable() -// logger.IfOne(true, true).Info("Logged") // Output: [app] INFO: Logged -// logger.IfOne(true, false).Info("Ignored") // No output -func (cl *Conditional) IfOne(conditions ...bool) *Conditional { - result := true - // Check each condition; set result to false if any is false - for _, cond := range conditions { - if !cond { - result = false - break - } - } - return &Conditional{logger: cl.logger, condition: result} -} - // IfAny creates a conditional logger that logs only if at least one condition is true. // It evaluates a variadic list of boolean conditions, setting the condition to true if any // is true (logical OR). Returns a new Conditional with the result. Thread-safe via the @@ -64,79 +43,117 @@ func (cl *Conditional) IfAny(conditions ...bool) *Conditional { return &Conditional{logger: cl.logger, condition: result} } -// Fields starts a fluent chain for adding fields using variadic key-value pairs, if the condition is true. -// It returns a FieldBuilder to attach fields, skipping field processing if the condition is false -// to optimize performance. Thread-safe via the FieldBuilder’s logger. +// IfErr creates a conditional logger that logs only if the error is non-nil. +// It's designed for the common pattern of checking errors before logging. // Example: // -// logger := New("app").Enable() -// logger.If(true).Fields("user", "alice").Info("Logged") // Output: [app] INFO: Logged [user=alice] -// logger.If(false).Fields("user", "alice").Info("Ignored") // No output, no field processing -func (cl *Conditional) Fields(pairs ...any) *FieldBuilder { - // Skip field processing if condition is false - if !cl.condition { - return &FieldBuilder{logger: cl.logger, fields: nil} - } - // Delegate to logger’s Fields method - return cl.logger.Fields(pairs...) +// err := doSomething() +// logger.IfErr(err).Error("Operation failed") // Only logs if err != nil +func (l *Logger) IfErr(err error) *Conditional { + return l.If(err != nil) } -// Field starts a fluent chain for adding fields from a map, if the condition is true. -// It returns a FieldBuilder to attach fields from a map, skipping processing if the condition -// is false. Thread-safe via the FieldBuilder’s logger. +// IfErrAny creates a conditional logger that logs only if AT LEAST ONE error is non-nil. +// It evaluates a variadic list of errors, setting the condition to true if any +// is non-nil (logical OR). Useful when any error should trigger logging. // Example: // -// logger := New("app").Enable() -// logger.If(true).Field(map[string]interface{}{"user": "alice"}).Info("Logged") // Output: [app] INFO: Logged [user=alice] -// logger.If(false).Field(map[string]interface{}{"user": "alice"}).Info("Ignored") // No output -func (cl *Conditional) Field(fields map[string]interface{}) *FieldBuilder { - // Skip field processing if condition is false - if !cl.condition { - return &FieldBuilder{logger: cl.logger, fields: nil} +// err1 := validate(input) +// err2 := authorize(user) +// logger.IfErrAny(err1, err2).Error("Either check failed") // Logs if EITHER error exists +func (l *Logger) IfErrAny(errs ...error) *Conditional { + for _, err := range errs { + if err != nil { + return l.If(true) // Any non-nil error makes it true + } } - // Delegate to logger’s Field method - return cl.logger.Field(fields) + return l.If(false) // False only if all errors are nil } -// Info logs a message at Info level with variadic arguments if the condition is true. -// It concatenates the arguments with spaces and delegates to the logger’s Info method if the -// condition is true. Skips processing if false, optimizing performance. Thread-safe via the -// logger’s log method. +// IfErrOne creates a conditional logger that logs only if ALL errors are non-nil. +// It evaluates a variadic list of errors, setting the condition to true only if +// all are non-nil (logical AND). Useful when you need all errors to be present. // Example: // -// logger := New("app").Enable() -// logger.If(true).Info("Action", "started") // Output: [app] INFO: Action started -// logger.If(false).Info("Action", "ignored") // No output -func (cl *Conditional) Info(args ...any) { - // Skip logging if condition is false - if !cl.condition { - return +// err1 := validate(input) +// err2 := authorize(user) +// logger.IfErrOne(err1, err2).Error("Both checks failed") // Logs only if BOTH errors exist +func (l *Logger) IfErrOne(errs ...error) *Conditional { + for _, err := range errs { + if err == nil { + return l.If(false) // Any nil error makes it false + } } - // Delegate to logger’s Info method - cl.logger.Info(args...) + return l.If(len(errs) > 0) // True only if we have at least one error and all are non-nil } -// Infof logs a message at Info level with a format string if the condition is true. -// It formats the message using the provided format string and arguments, delegating to the -// logger’s Infof method if the condition is true. Skips processing if false, optimizing performance. -// Thread-safe via the logger’s log method. +// IfErr creates a conditional logger that logs only if the error is non-nil. +// Returns a new Conditional with the error check result. +// Example: +// +// err := doSomething() +// logger.If(true).IfErr(err).Error("Failed") // Only logs if condition true AND err != nil +func (cl *Conditional) IfErr(err error) *Conditional { + return cl.IfOne(err != nil) +} + +// IfErrAny creates a conditional logger that logs only if AT LEAST ONE error is non-nil. +// Returns a new Conditional with the logical OR result of error checks. +// Example: +// +// err1 := validate(input) +// err2 := authorize(user) +// logger.If(true).IfErrAny(err1, err2).Error("Either failed") // Logs if condition true AND either error exists +func (cl *Conditional) IfErrAny(errs ...error) *Conditional { + for _, err := range errs { + if err != nil { + return &Conditional{logger: cl.logger, condition: cl.condition && true} + } + } + return &Conditional{logger: cl.logger, condition: false} +} + +// IfErrOne creates a conditional logger that logs only if ALL errors are non-nil. +// Returns a new Conditional with the logical AND result of error checks. +// Example: +// +// err1 := validate(input) +// err2 := authorize(user) +// logger.If(true).IfErrOne(err1, err2).Error("Both failed") // Logs if condition true AND both errors exist +func (cl *Conditional) IfErrOne(errs ...error) *Conditional { + for _, err := range errs { + if err == nil { + return &Conditional{logger: cl.logger, condition: false} + } + } + return &Conditional{logger: cl.logger, condition: cl.condition && len(errs) > 0} +} + +// IfOne creates a conditional logger that logs only if all conditions are true. +// It evaluates a variadic list of boolean conditions, setting the condition to true only if +// all are true (logical AND). Returns a new Conditional with the result. Thread-safe via the +// underlying logger. // Example: // // logger := New("app").Enable() -// logger.If(true).Infof("Action %s", "started") // Output: [app] INFO: Action started -// logger.If(false).Infof("Action %s", "ignored") // No output -func (cl *Conditional) Infof(format string, args ...any) { - // Skip logging if condition is false - if !cl.condition { - return +// logger.IfOne(true, true).Info("Logged") // Output: [app] INFO: Logged +// logger.IfOne(true, false).Info("Ignored") // No output +func (cl *Conditional) IfOne(conditions ...bool) *Conditional { + result := true + // Check each condition; set result to false if any is false + for _, cond := range conditions { + if !cond { + result = false + break + } } - // Delegate to logger’s Infof method - cl.logger.Infof(format, args...) + return &Conditional{logger: cl.logger, condition: result} } // Debug logs a message at Debug level with variadic arguments if the condition is true. -// It concatenates the arguments with spaces and delegates to the logger’s Debug method if the -// condition is true. Skips processing if false. Thread-safe via the logger’s log method. +// It concatenates the arguments with spaces and delegates to the logger's Debug method if the +// condition is true. Skips processing if false, optimizing performance. Thread-safe via the +// logger's log method. // Example: // // logger := New("app").Enable().Level(lx.LevelDebug) @@ -147,13 +164,13 @@ func (cl *Conditional) Debug(args ...any) { if !cl.condition { return } - // Delegate to logger’s Debug method + // Delegate to logger's Debug method cl.logger.Debug(args...) } // Debugf logs a message at Debug level with a format string if the condition is true. -// It formats the message and delegates to the logger’s Debugf method if the condition is true. -// Skips processing if false. Thread-safe via the logger’s log method. +// It formats the message and delegates to the logger's Debugf method if the condition is true. +// Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable().Level(lx.LevelDebug) @@ -164,47 +181,13 @@ func (cl *Conditional) Debugf(format string, args ...any) { if !cl.condition { return } - // Delegate to logger’s Debugf method + // Delegate to logger's Debugf method cl.logger.Debugf(format, args...) } -// Warn logs a message at Warn level with variadic arguments if the condition is true. -// It concatenates the arguments with spaces and delegates to the logger’s Warn method if the -// condition is true. Skips processing if false. Thread-safe via the logger’s log method. -// Example: -// -// logger := New("app").Enable() -// logger.If(true).Warn("Warning", "issued") // Output: [app] WARN: Warning issued -// logger.If(false).Warn("Warning", "ignored") // No output -func (cl *Conditional) Warn(args ...any) { - // Skip logging if condition is false - if !cl.condition { - return - } - // Delegate to logger’s Warn method - cl.logger.Warn(args...) -} - -// Warnf logs a message at Warn level with a format string if the condition is true. -// It formats the message and delegates to the logger’s Warnf method if the condition is true. -// Skips processing if false. Thread-safe via the logger’s log method. -// Example: -// -// logger := New("app").Enable() -// logger.If(true).Warnf("Warning %s", "issued") // Output: [app] WARN: Warning issued -// logger.If(false).Warnf("Warning %s", "ignored") // No output -func (cl *Conditional) Warnf(format string, args ...any) { - // Skip logging if condition is false - if !cl.condition { - return - } - // Delegate to logger’s Warnf method - cl.logger.Warnf(format, args...) -} - // Error logs a message at Error level with variadic arguments if the condition is true. -// It concatenates the arguments with spaces and delegates to the logger’s Error method if the -// condition is true. Skips processing if false. Thread-safe via the logger’s log method. +// It concatenates the arguments with spaces and delegates to the logger's Error method if the +// condition is true. Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -215,13 +198,13 @@ func (cl *Conditional) Error(args ...any) { if !cl.condition { return } - // Delegate to logger’s Error method + // Delegate to logger's Error method cl.logger.Error(args...) } // Errorf logs a message at Error level with a format string if the condition is true. -// It formats the message and delegates to the logger’s Errorf method if the condition is true. -// Skips processing if false. Thread-safe via the logger’s log method. +// It formats the message and delegates to the logger's Errorf method if the condition is true. +// Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -232,48 +215,14 @@ func (cl *Conditional) Errorf(format string, args ...any) { if !cl.condition { return } - // Delegate to logger’s Errorf method + // Delegate to logger's Errorf method cl.logger.Errorf(format, args...) } -// Stack logs a message at Error level with a stack trace and variadic arguments if the condition is true. -// It concatenates the arguments with spaces and delegates to the logger’s Stack method if the -// condition is true. Skips processing if false. Thread-safe via the logger’s log method. -// Example: -// -// logger := New("app").Enable() -// logger.If(true).Stack("Critical", "error") // Output: [app] ERROR: Critical error [stack=...] -// logger.If(false).Stack("Critical", "ignored") // No output -func (cl *Conditional) Stack(args ...any) { - // Skip logging if condition is false - if !cl.condition { - return - } - // Delegate to logger’s Stack method - cl.logger.Stack(args...) -} - -// Stackf logs a message at Error level with a stack trace and a format string if the condition is true. -// It formats the message and delegates to the logger’s Stackf method if the condition is true. -// Skips processing if false. Thread-safe via the logger’s log method. -// Example: -// -// logger := New("app").Enable() -// logger.If(true).Stackf("Critical %s", "error") // Output: [app] ERROR: Critical error [stack=...] -// logger.If(false).Stackf("Critical %s", "ignored") // No output -func (cl *Conditional) Stackf(format string, args ...any) { - // Skip logging if condition is false - if !cl.condition { - return - } - // Delegate to logger’s Stackf method - cl.logger.Stackf(format, args...) -} - // Fatal logs a message at Error level with a stack trace and variadic arguments if the condition is true, -// then exits. It concatenates the arguments with spaces and delegates to the logger’s Fatal method +// then exits. It concatenates the arguments with spaces and delegates to the logger's Fatal method // if the condition is true, terminating the program with exit code 1. Skips processing if false. -// Thread-safe via the logger’s log method. +// Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -284,13 +233,13 @@ func (cl *Conditional) Fatal(args ...any) { if !cl.condition { return } - // Delegate to logger’s Fatal method + // Delegate to logger's Fatal method cl.logger.Fatal(args...) } // Fatalf logs a formatted message at Error level with a stack trace if the condition is true, then exits. -// It formats the message and delegates to the logger’s Fatalf method if the condition is true, -// terminating the program with exit code 1. Skips processing if false. Thread-safe via the logger’s log method. +// It formats the message and delegates to the logger's Fatalf method if the condition is true, +// terminating the program with exit code 1. Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -301,13 +250,83 @@ func (cl *Conditional) Fatalf(format string, args ...any) { if !cl.condition { return } - // Delegate to logger’s Fatalf method + // Delegate to logger's Fatalf method cl.logger.Fatalf(format, args...) } +// Field starts a fluent chain for adding fields from a map, if the condition is true. +// It returns a FieldBuilder to attach fields from a map, skipping processing if the condition +// is false. Thread-safe via the FieldBuilder's logger. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Field(map[string]interface{}{"user": "alice"}).Info("Logged") // Output: [app] INFO: Logged [user=alice] +// logger.If(false).Field(map[string]interface{}{"user": "alice"}).Info("Ignored") // No output +func (cl *Conditional) Field(fields map[string]interface{}) *FieldBuilder { + // Skip field processing if condition is false + if !cl.condition { + return &FieldBuilder{logger: cl.logger, fields: nil} + } + // Delegate to logger's Field method + return cl.logger.Field(fields) +} + +// Fields starts a fluent chain for adding fields using variadic key-value pairs, if the condition is true. +// It returns a FieldBuilder to attach fields, skipping field processing if the condition is false +// to optimize performance. Thread-safe via the FieldBuilder's logger. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Fields("user", "alice").Info("Logged") // Output: [app] INFO: Logged [user=alice] +// logger.If(false).Fields("user", "alice").Info("Ignored") // No output, no field processing +func (cl *Conditional) Fields(pairs ...any) *FieldBuilder { + // Skip field processing if condition is false + if !cl.condition { + return &FieldBuilder{logger: cl.logger, fields: nil} + } + // Delegate to logger's Fields method + return cl.logger.Fields(pairs...) +} + +// Info logs a message at Info level with variadic arguments if the condition is true. +// It concatenates the arguments with spaces and delegates to the logger's Info method if the +// condition is true. Skips processing if false, optimizing performance. Thread-safe via the +// logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Info("Action", "started") // Output: [app] INFO: Action started +// logger.If(false).Info("Action", "ignored") // No output +func (cl *Conditional) Info(args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Info method + cl.logger.Info(args...) +} + +// Infof logs a message at Info level with a format string if the condition is true. +// It formats the message using the provided format string and arguments, delegating to the +// logger's Infof method if the condition is true. Skips processing if false, optimizing performance. +// Thread-safe via the logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Infof("Action %s", "started") // Output: [app] INFO: Action started +// logger.If(false).Infof("Action %s", "ignored") // No output +func (cl *Conditional) Infof(format string, args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Infof method + cl.logger.Infof(format, args...) +} + // Panic logs a message at Error level with a stack trace and variadic arguments if the condition is true, -// then panics. It concatenates the arguments with spaces and delegates to the logger’s Panic method -// if the condition is true, triggering a panic. Skips processing if false. Thread-safe via the logger’s log method. +// then panics. It concatenates the arguments with spaces and delegates to the logger's Panic method +// if the condition is true, triggering a panic. Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -318,13 +337,13 @@ func (cl *Conditional) Panic(args ...any) { if !cl.condition { return } - // Delegate to logger’s Panic method + // Delegate to logger's Panic method cl.logger.Panic(args...) } // Panicf logs a formatted message at Error level with a stack trace if the condition is true, then panics. -// It formats the message and delegates to the logger’s Panicf method if the condition is true, -// triggering a panic. Skips processing if false. Thread-safe via the logger’s log method. +// It formats the message and delegates to the logger's Panicf method if the condition is true, +// triggering a panic. Skips processing if false. Thread-safe via the logger's log method. // Example: // // logger := New("app").Enable() @@ -335,6 +354,74 @@ func (cl *Conditional) Panicf(format string, args ...any) { if !cl.condition { return } - // Delegate to logger’s Panicf method + // Delegate to logger's Panicf method cl.logger.Panicf(format, args...) } + +// Stack logs a message at Error level with a stack trace and variadic arguments if the condition is true. +// It concatenates the arguments with spaces and delegates to the logger's Stack method if the +// condition is true. Skips processing if false. Thread-safe via the logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Stack("Critical", "error") // Output: [app] ERROR: Critical error [stack=...] +// logger.If(false).Stack("Critical", "ignored") // No output +func (cl *Conditional) Stack(args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Stack method + cl.logger.Stack(args...) +} + +// Stackf logs a message at Error level with a stack trace and a format string if the condition is true. +// It formats the message and delegates to the logger's Stackf method if the condition is true. +// Skips processing if false. Thread-safe via the logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Stackf("Critical %s", "error") // Output: [app] ERROR: Critical error [stack=...] +// logger.If(false).Stackf("Critical %s", "ignored") // No output +func (cl *Conditional) Stackf(format string, args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Stackf method + cl.logger.Stackf(format, args...) +} + +// Warn logs a message at Warn level with variadic arguments if the condition is true. +// It concatenates the arguments with spaces and delegates to the logger's Warn method if the +// condition is true. Skips processing if false. Thread-safe via the logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Warn("Warning", "issued") // Output: [app] WARN: Warning issued +// logger.If(false).Warn("Warning", "ignored") // No output +func (cl *Conditional) Warn(args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Warn method + cl.logger.Warn(args...) +} + +// Warnf logs a message at Warn level with a format string if the condition is true. +// It formats the message and delegates to the logger's Warnf method if the condition is true. +// Skips processing if false. Thread-safe via the logger's log method. +// Example: +// +// logger := New("app").Enable() +// logger.If(true).Warnf("Warning %s", "issued") // Output: [app] WARN: Warning issued +// logger.If(false).Warnf("Warning %s", "ignored") // No output +func (cl *Conditional) Warnf(format string, args ...any) { + // Skip logging if condition is false + if !cl.condition { + return + } + // Delegate to logger's Warnf method + cl.logger.Warnf(format, args...) +} diff --git a/vendor/github.com/olekukonko/ll/dbg.go b/vendor/github.com/olekukonko/ll/dbg.go new file mode 100644 index 0000000000..d3d8c42b43 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/dbg.go @@ -0,0 +1,282 @@ +package ll + +import ( + "container/list" + "fmt" + "os" + "runtime" + "strings" + "sync" + + "github.com/olekukonko/ll/lx" +) + +// ----------------------------------------------------------------------------- +// Global Cache Instance +// ----------------------------------------------------------------------------- + +// sourceCache caches up to 128 source files using LRU eviction. +var sourceCache = newFileLRU(128) + +// ----------------------------------------------------------------------------- +// File-Level LRU Cache +// ----------------------------------------------------------------------------- + +type fileLRU struct { + capacity int + mu sync.Mutex + list *list.List + items map[string]*list.Element +} + +type fileItem struct { + key string + lines []string +} + +func newFileLRU(capacity int) *fileLRU { + if capacity <= 0 { + capacity = 1 + } + return &fileLRU{ + capacity: capacity, + list: list.New(), + items: make(map[string]*list.Element, capacity), + } +} + +// getLine retrieves a specific 1-indexed line from a file. +func (c *fileLRU) getLine(file string, line int) (string, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + // 1. Cache Hit + if elem, ok := c.items[file]; ok { + c.list.MoveToFront(elem) + item := elem.Value.(*fileItem) + if item.lines == nil { + return "", false + } + return nthLine(item.lines, line) + } + + // 2. Cache Miss - Read File + // Release lock during I/O to avoid blocking other loggers + c.mu.Unlock() + data, err := os.ReadFile(file) + c.mu.Lock() + + // 3. Double-check (another goroutine might have loaded it while unlocked) + if elem, ok := c.items[file]; ok { + c.list.MoveToFront(elem) + item := elem.Value.(*fileItem) + if item.lines == nil { + return "", false + } + return nthLine(item.lines, line) + } + + var lines []string + if err == nil { + lines = strings.Split(string(data), "\n") + } + + // 4. Store (Positive or Negative Cache) + item := &fileItem{ + key: file, + lines: lines, + } + elem := c.list.PushFront(item) + c.items[file] = elem + + // 5. Evict if needed + if c.list.Len() > c.capacity { + old := c.list.Back() + if old != nil { + c.list.Remove(old) + delete(c.items, old.Value.(*fileItem).key) + } + } + + if lines == nil { + return "", false + } + return nthLine(lines, line) +} + +// nthLine returns the 1-indexed line from slice. +func nthLine(lines []string, n int) (string, bool) { + if n <= 0 || n > len(lines) { + return "", false + } + return strings.TrimSuffix(lines[n-1], "\r"), true +} + +// ----------------------------------------------------------------------------- +// Logger Debug Implementation +// ----------------------------------------------------------------------------- + +// Dbg logs debug information including source file, line number, +// and the best-effort extracted expression. +// +// Example: +// +// x := 42 +// logger.Dbg("val", x) +// Output: [file.go:123] "val" = "val", x = 42 +func (l *Logger) Dbg(values ...interface{}) { + if !l.shouldLog(lx.LevelInfo) { + return + } + l.dbg(2, values...) +} + +func (l *Logger) dbg(skip int, values ...interface{}) { + file, line, ok := callerFrame(skip) + if !ok { + // Fallback if we can't get frame + var sb strings.Builder + sb.WriteString("[?:?] ") + for i, v := range values { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(fmt.Sprintf("%+v", v)) + } + l.log(lx.LevelInfo, lx.ClassText, sb.String(), nil, false) + return + } + + shortFile := file + if idx := strings.LastIndex(file, "/"); idx >= 0 { + shortFile = file[idx+1:] + } + + srcLine, hit := sourceCache.getLine(file, line) + + var expr string + if hit && srcLine != "" { + // Attempt to extract the text inside Dbg(...) + if a := strings.Index(srcLine, "Dbg("); a >= 0 { + rest := srcLine[a+len("Dbg("):] + if b := strings.LastIndex(rest, ")"); b >= 0 { + expr = strings.TrimSpace(rest[:b]) + } + } else { + // Fallback: extract first (...) group if Dbg isn't explicit prefix + a := strings.Index(srcLine, "(") + b := strings.LastIndex(srcLine, ")") + if a >= 0 && b > a { + expr = strings.TrimSpace(srcLine[a+1 : b]) + } + } + } + + // Format output + var outBuilder strings.Builder + outBuilder.WriteString(fmt.Sprintf("[%s:%d] ", shortFile, line)) + + // Attempt to split expressions to map 1:1 with values + var parts []string + if expr != "" { + parts = splitExpressions(expr) + } + + // If the number of extracted expressions matches the number of values, + // print them as "expr = value". Otherwise, fall back to "expr = val1, val2". + if len(parts) == len(values) { + for i, v := range values { + if i > 0 { + outBuilder.WriteString(", ") + } + outBuilder.WriteString(fmt.Sprintf("%s = %+v", parts[i], v)) + } + } else { + if expr != "" { + outBuilder.WriteString(expr) + outBuilder.WriteString(" = ") + } + for i, v := range values { + if i > 0 { + outBuilder.WriteString(", ") + } + outBuilder.WriteString(fmt.Sprintf("%+v", v)) + } + } + + l.log(lx.LevelInfo, lx.ClassDbg, outBuilder.String(), nil, false) +} + +// splitExpressions splits a comma-separated string of expressions, +// respecting nested parentheses, brackets, braces, and quotes. +// Example: "a, fn(b, c), d" -> ["a", "fn(b, c)", "d"] +func splitExpressions(s string) []string { + var parts []string + var current strings.Builder + depth := 0 // Tracks nested (), [], {} + inQuote := false // Tracks string literals + var quoteChar rune + + for _, r := range s { + switch { + case inQuote: + current.WriteRune(r) + if r == quoteChar { + // We rely on the fact that valid Go source won't have unescaped quotes easily + // accessible here without complex parsing, but for simple Dbg calls this suffices. + // A robust parser handles `\"`, but simple state toggling covers 99% of debug cases. + inQuote = false + } + case r == '"' || r == '\'': + inQuote = true + quoteChar = r + current.WriteRune(r) + case r == '(' || r == '{' || r == '[': + depth++ + current.WriteRune(r) + case r == ')' || r == '}' || r == ']': + depth-- + current.WriteRune(r) + case r == ',' && depth == 0: + // Split point + parts = append(parts, strings.TrimSpace(current.String())) + current.Reset() + default: + current.WriteRune(r) + } + } + if current.Len() > 0 { + parts = append(parts, strings.TrimSpace(current.String())) + } + return parts +} + +// ----------------------------------------------------------------------------- +// Caller Resolution +// ----------------------------------------------------------------------------- + +// callerFrame walks stack frames until it finds the first frame +// outside the ll package. +func callerFrame(skip int) (file string, line int, ok bool) { + // +2 to skip callerFrame + dbg itself. + pcs := make([]uintptr, 32) + n := runtime.Callers(skip+2, pcs) + if n == 0 { + return "", 0, false + } + + frames := runtime.CallersFrames(pcs[:n]) + for { + fr, more := frames.Next() + // fr.Function looks like: "github.com/you/mod/ll.(*Logger).Dbg" + // We want the first frame that is NOT inside package ll. + if fr.Function == "" || !strings.Contains(fr.Function, "/ll.") && !strings.Contains(fr.Function, ".ll.") { + return fr.File, fr.Line, true + } + + if !more { + // Fallback: return the last frame we saw + return fr.File, fr.Line, fr.File != "" + } + } +} diff --git a/vendor/github.com/olekukonko/ll/field.go b/vendor/github.com/olekukonko/ll/field.go index 4162162ff7..ed18b529aa 100644 --- a/vendor/github.com/olekukonko/ll/field.go +++ b/vendor/github.com/olekukonko/ll/field.go @@ -1,11 +1,13 @@ +// field.go package ll import ( "fmt" - "github.com/olekukonko/cat" - "github.com/olekukonko/ll/lx" "os" "strings" + + "github.com/olekukonko/cat" + "github.com/olekukonko/ll/lx" ) // FieldBuilder enables fluent addition of fields before logging. @@ -13,12 +15,12 @@ import ( // supporting structured logging with metadata. The builder allows chaining to add fields // and log messages at various levels (Info, Debug, Warn, Error, etc.) in a single expression. type FieldBuilder struct { - logger *Logger // Associated logger instance for logging operations - fields map[string]interface{} // Fields to include in the log entry as key-value pairs + logger *Logger // Associated logger instance for logging operations + fields lx.Fields // Fields to include in the log entry as ordered key-value pairs } -// Logger creates a new logger with the builder’s fields embedded in its context. -// It clones the parent logger and copies the builder’s fields into the new logger’s context, +// Logger creates a new logger with the builder's fields embedded in its context. +// It clones the parent logger and copies the builder's fields into the new logger's context, // enabling persistent field inclusion in subsequent logs. This method supports fluent chaining // after Fields or Field calls. // Example: @@ -29,17 +31,14 @@ type FieldBuilder struct { func (fb *FieldBuilder) Logger() *Logger { // Clone the parent logger to preserve its configuration newLogger := fb.logger.Clone() - // Initialize a new context map to avoid modifying the parent’s context - newLogger.context = make(map[string]interface{}) - // Copy builder’s fields into the new logger’s context - for k, v := range fb.fields { - newLogger.context[k] = v - } + // Copy builder's fields into the new logger's context + newLogger.context = make(lx.Fields, len(fb.fields)) + copy(newLogger.context, fb.fields) return newLogger } -// Info logs a message at Info level with the builder’s fields. -// It concatenates the arguments with spaces and delegates to the logger’s log method, +// Info logs a message at Info level with the builder's fields. +// It concatenates the arguments with spaces and delegates to the logger's log method, // returning early if fields are nil. This method is used for informational messages. // Example: // @@ -50,13 +49,13 @@ func (fb *FieldBuilder) Info(args ...any) { if fb.fields == nil { return } - // Log at Info level with the builder’s fields, no stack trace + // Log at Info level with the builder's fields, no stack trace fb.logger.log(lx.LevelInfo, lx.ClassText, cat.Space(args...), fb.fields, false) } -// Infof logs a message at Info level with the builder’s fields. +// Infof logs a message at Info level with the builder's fields. // It formats the message using the provided format string and arguments, then delegates -// to the logger’s internal log method. If fields are nil, it returns early to avoid logging. +// to the logger's internal log method. If fields are nil, it returns early to avoid logging. // This method is part of the fluent API, typically called after adding fields. // Example: // @@ -69,12 +68,12 @@ func (fb *FieldBuilder) Infof(format string, args ...any) { } // Format the message using the provided arguments msg := fmt.Sprintf(format, args...) - // Log at Info level with the builder’s fields, no stack trace + // Log at Info level with the builder's fields, no stack trace fb.logger.log(lx.LevelInfo, lx.ClassText, msg, fb.fields, false) } -// Debug logs a message at Debug level with the builder’s fields. -// It concatenates the arguments with spaces and delegates to the logger’s log method, +// Debug logs a message at Debug level with the builder's fields. +// It concatenates the arguments with spaces and delegates to the logger's log method, // returning early if fields are nil. This method is used for debugging information. // Example: // @@ -85,12 +84,12 @@ func (fb *FieldBuilder) Debug(args ...any) { if fb.fields == nil { return } - // Log at Debug level with the builder’s fields, no stack trace + // Log at Debug level with the builder's fields, no stack trace fb.logger.log(lx.LevelDebug, lx.ClassText, cat.Space(args...), fb.fields, false) } -// Debugf logs a message at Debug level with the builder’s fields. -// It formats the message and delegates to the logger’s log method, returning early if +// Debugf logs a message at Debug level with the builder's fields. +// It formats the message and delegates to the logger's log method, returning early if // fields are nil. This method is used for debugging information that may be disabled in // production environments. // Example: @@ -104,12 +103,12 @@ func (fb *FieldBuilder) Debugf(format string, args ...any) { } // Format the message msg := fmt.Sprintf(format, args...) - // Log at Debug level with the builder’s fields, no stack trace + // Log at Debug level with the builder's fields, no stack trace fb.logger.log(lx.LevelDebug, lx.ClassText, msg, fb.fields, false) } -// Warn logs a message at Warn level with the builder’s fields. -// It concatenates the arguments with spaces and delegates to the logger’s log method, +// Warn logs a message at Warn level with the builder's fields. +// It concatenates the arguments with spaces and delegates to the logger's log method, // returning early if fields are nil. This method is used for warning conditions. // Example: // @@ -120,12 +119,12 @@ func (fb *FieldBuilder) Warn(args ...any) { if fb.fields == nil { return } - // Log at Warn level with the builder’s fields, no stack trace + // Log at Warn level with the builder's fields, no stack trace fb.logger.log(lx.LevelWarn, lx.ClassText, cat.Space(args...), fb.fields, false) } -// Warnf logs a message at Warn level with the builder’s fields. -// It formats the message and delegates to the logger’s log method, returning early if +// Warnf logs a message at Warn level with the builder's fields. +// It formats the message and delegates to the logger's log method, returning early if // fields are nil. This method is used for warning conditions that do not halt execution. // Example: // @@ -138,12 +137,12 @@ func (fb *FieldBuilder) Warnf(format string, args ...any) { } // Format the message msg := fmt.Sprintf(format, args...) - // Log at Warn level with the builder’s fields, no stack trace + // Log at Warn level with the builder's fields, no stack trace fb.logger.log(lx.LevelWarn, lx.ClassText, msg, fb.fields, false) } -// Error logs a message at Error level with the builder’s fields. -// It concatenates the arguments with spaces and delegates to the logger’s log method, +// Error logs a message at Error level with the builder's fields. +// It concatenates the arguments with spaces and delegates to the logger's log method, // returning early if fields are nil. This method is used for error conditions. // Example: // @@ -154,12 +153,12 @@ func (fb *FieldBuilder) Error(args ...any) { if fb.fields == nil { return } - // Log at Error level with the builder’s fields, no stack trace + // Log at Error level with the builder's fields, no stack trace fb.logger.log(lx.LevelError, lx.ClassText, cat.Space(args...), fb.fields, false) } -// Errorf logs a message at Error level with the builder’s fields. -// It formats the message and delegates to the logger’s log method, returning early if +// Errorf logs a message at Error level with the builder's fields. +// It formats the message and delegates to the logger's log method, returning early if // fields are nil. This method is used for error conditions that may require attention. // Example: // @@ -172,12 +171,12 @@ func (fb *FieldBuilder) Errorf(format string, args ...any) { } // Format the message msg := fmt.Sprintf(format, args...) - // Log at Error level with the builder’s fields, no stack trace + // Log at Error level with the builder's fields, no stack trace fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, false) } -// Stack logs a message at Error level with a stack trace and the builder’s fields. -// It concatenates the arguments with spaces and delegates to the logger’s log method, +// Stack logs a message at Error level with a stack trace and the builder's fields. +// It concatenates the arguments with spaces and delegates to the logger's log method, // returning early if fields are nil. This method is useful for debugging critical errors. // Example: // @@ -188,12 +187,12 @@ func (fb *FieldBuilder) Stack(args ...any) { if fb.fields == nil { return } - // Log at Error level with the builder’s fields and a stack trace + // Log at Error level with the builder's fields and a stack trace fb.logger.log(lx.LevelError, lx.ClassText, cat.Space(args...), fb.fields, true) } -// Stackf logs a message at Error level with a stack trace and the builder’s fields. -// It formats the message and delegates to the logger’s log method, returning early if +// Stackf logs a message at Error level with a stack trace and the builder's fields. +// It formats the message and delegates to the logger's log method, returning early if // fields are nil. This method is useful for debugging critical errors. // Example: // @@ -206,11 +205,11 @@ func (fb *FieldBuilder) Stackf(format string, args ...any) { } // Format the message msg := fmt.Sprintf(format, args...) - // Log at Error level with the builder’s fields and a stack trace + // Log at Error level with the builder's fields and a stack trace fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, true) } -// Fatal logs a message at Error level with a stack trace and the builder’s fields, then exits. +// Fatal logs a message at Error level with a stack trace and the builder's fields, then exits. // It constructs the message from variadic arguments, logs it with a stack trace, and terminates // the program with exit code 1. Returns early if fields are nil. This method is used for // unrecoverable errors. @@ -231,13 +230,16 @@ func (fb *FieldBuilder) Fatal(args ...any) { } builder.WriteString(fmt.Sprint(arg)) } - // Log at Error level with the builder’s fields and a stack trace - fb.logger.log(lx.LevelError, lx.ClassText, builder.String(), fb.fields, true) + // Log at Error level with the builder's fields and a stack trace + fb.logger.log(lx.LevelFatal, lx.ClassText, builder.String(), fb.fields, fb.logger.fatalStack) + // Exit the program with status code 1 - os.Exit(1) + if fb.logger.fatalExits { + os.Exit(1) + } } -// Fatalf logs a formatted message at Error level with a stack trace and the builder’s fields, +// Fatalf logs a formatted message at Error level with a stack trace and the builder's fields, // then exits. It delegates to Fatal and returns early if fields are nil. This method is used // for unrecoverable errors. // Example: @@ -253,7 +255,7 @@ func (fb *FieldBuilder) Fatalf(format string, args ...any) { fb.Fatal(fmt.Sprintf(format, args...)) } -// Panic logs a message at Error level with a stack trace and the builder’s fields, then panics. +// Panic logs a message at Error level with a stack trace and the builder's fields, then panics. // It constructs the message from variadic arguments, logs it with a stack trace, and triggers // a panic with the message. Returns early if fields are nil. This method is used for critical // errors that require immediate program termination with a panic. @@ -275,13 +277,13 @@ func (fb *FieldBuilder) Panic(args ...any) { builder.WriteString(fmt.Sprint(arg)) } msg := builder.String() - // Log at Error level with the builder’s fields and a stack trace + // Log at Error level with the builder's fields and a stack trace fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, true) // Trigger a panic with the formatted message panic(msg) } -// Panicf logs a formatted message at Error level with a stack trace and the builder’s fields, +// Panicf logs a formatted message at Error level with a stack trace and the builder's fields, // then panics. It delegates to Panic and returns early if fields are nil. This method is used // for critical errors that require immediate program termination with a panic. // Example: @@ -301,7 +303,7 @@ func (fb *FieldBuilder) Panicf(format string, args ...any) { // It stores non-nil errors in the "error" field: a single error if only one is non-nil, // or a slice of errors if multiple are non-nil. It logs the concatenated string representations // of non-nil errors (e.g., "failed 1; failed 2") at the Error level. Returns the FieldBuilder -// for chaining, allowing further field additions or logging. Thread-safe via the logger’s mutex. +// for chaining, allowing further field additions or logging. Thread-safe via the logger's mutex. // Example: // // logger := New("app").Enable() @@ -311,9 +313,9 @@ func (fb *FieldBuilder) Panicf(format string, args ...any) { // // Output: [app] ERROR: failed 1; failed 2 // // [app] INFO: Error occurred [error=[failed 1 failed 2] k=v] func (fb *FieldBuilder) Err(errs ...error) *FieldBuilder { - // Initialize fields map if nil + // Initialize fields slice if nil if fb.fields == nil { - fb.fields = make(map[string]interface{}) + fb.fields = make(lx.Fields, 0, 4) } // Collect non-nil errors and build log message @@ -335,10 +337,10 @@ func (fb *FieldBuilder) Err(errs ...error) *FieldBuilder { if count > 0 { if count == 1 { // Store single error directly - fb.fields["error"] = nonNilErrors[0] + fb.fields = append(fb.fields, lx.Field{Key: "error", Value: nonNilErrors[0]}) } else { // Store slice of errors - fb.fields["error"] = nonNilErrors + fb.fields = append(fb.fields, lx.Field{Key: "error", Value: nonNilErrors}) } // Log concatenated error messages at Error level fb.logger.log(lx.LevelError, lx.ClassText, builder.String(), nil, false) @@ -357,19 +359,30 @@ func (fb *FieldBuilder) Err(errs ...error) *FieldBuilder { // logger := New("app").Enable() // logger.Fields("k1", "v1").Merge("k2", "v2").Info("Action") // Output: [app] INFO: Action [k1=v1 k2=v2] func (fb *FieldBuilder) Merge(pairs ...any) *FieldBuilder { + // Initialize fields slice if nil + if fb.fields == nil { + fb.fields = make(lx.Fields, 0, len(pairs)/2) + } + // Process pairs as key-value, advancing by 2 for i := 0; i < len(pairs)-1; i += 2 { // Ensure the key is a string if key, ok := pairs[i].(string); ok { - fb.fields[key] = pairs[i+1] + fb.fields = append(fb.fields, lx.Field{Key: key, Value: pairs[i+1]}) } else { // Log an error field for non-string keys - fb.fields["error"] = fmt.Errorf("non-string key in Merge: %v", pairs[i]) + fb.fields = append(fb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("non-string key in Merge: %v", pairs[i]), + }) } } // Check for uneven pairs (missing value) if len(pairs)%2 != 0 { - fb.fields["error"] = fmt.Errorf("uneven key-value pairs in Merge: [%v]", pairs[len(pairs)-1]) + fb.fields = append(fb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("uneven key-value pairs in Merge: [%v]", pairs[len(pairs)-1]), + }) } return fb } diff --git a/vendor/github.com/olekukonko/ll/global.go b/vendor/github.com/olekukonko/ll/global.go index 178de8fe4a..edc41fbcb9 100644 --- a/vendor/github.com/olekukonko/ll/global.go +++ b/vendor/github.com/olekukonko/ll/global.go @@ -1,11 +1,9 @@ package ll import ( - "os" "sync/atomic" "time" - "github.com/olekukonko/ll/lh" "github.com/olekukonko/ll/lx" ) @@ -14,16 +12,7 @@ import ( // a logger instance. The logger is initialized with default settings: enabled, Debug level, // flat namespace style, and a text handler to os.Stdout. It is thread-safe due to the Logger // struct’s mutex. -var defaultLogger = &Logger{ - enabled: true, // Initially enabled - level: lx.LevelDebug, // Minimum log level set to Debug - namespaces: defaultStore, // Shared namespace store for enable/disable states - context: make(map[string]interface{}), // Empty context for global fields - style: lx.FlatPath, // Flat namespace style (e.g., [parent/child]) - handler: lh.NewTextHandler(os.Stdout), // Default text handler to os.Stdout - middleware: make([]Middleware, 0), // Empty middleware chain - stackBufferSize: 4096, // Buffer size for stack traces -} +var defaultLogger = New("") // Handler sets the handler for the default logger. // It configures the output destination and format (e.g., text, JSON) for logs emitted by @@ -233,16 +222,25 @@ func Panicf(format string, args ...any) { } // If creates a conditional logger that logs only if the condition is true using the default logger. -// It returns a Conditional struct that wraps the default logger, enabling conditional logging methods. -// Thread-safe via the Logger’s mutex. -// Example: -// -// ll.If(true).Info("Logged") // Output: [] INFO: Logged -// ll.If(false).Info("Ignored") // No output func If(condition bool) *Conditional { return defaultLogger.If(condition) } +// IfErr creates a conditional logger that logs only if the error is non-nil using the default logger. +func IfErr(err error) *Conditional { + return defaultLogger.IfErr(err) +} + +// IfErrAny creates a conditional logger that logs only if AT LEAST ONE error is non-nil using the default logger. +func IfErrAny(errs ...error) *Conditional { + return defaultLogger.IfErrAny(errs...) +} + +// IfErrOne creates a conditional logger that logs only if ALL errors are non-nil using the default logger. +func IfErrOne(errs ...error) *Conditional { + return defaultLogger.IfErrOne(errs...) +} + // Context creates a new logger with additional contextual fields using the default logger. // It preserves existing context fields and adds new ones, returning a new logger instance // to avoid mutating the default logger. Thread-safe with write lock. @@ -260,8 +258,8 @@ func Context(fields map[string]interface{}) *Logger { // // ll.AddContext("user", "alice") // ll.Info("Action") // Output: [] INFO: Action [user=alice] -func AddContext(key string, value interface{}) *Logger { - return defaultLogger.AddContext(key, value) +func AddContext(pairs ...any) *Logger { + return defaultLogger.AddContext(pairs...) } // GetContext returns the default logger’s context map of persistent key-value fields. @@ -269,7 +267,7 @@ func AddContext(key string, value interface{}) *Logger { // Example: // // ll.AddContext("user", "alice") -// ctx := ll.GetContext() // Returns map[string]interface{}{"user": "alice"} +// ctx := ll.GetContext() // Returns map[string]interface{}{"user": "alice"}k func GetContext() map[string]interface{} { return defaultLogger.GetContext() } @@ -472,6 +470,37 @@ func Measure(fns ...func()) time.Duration { return defaultLogger.Measure(fns...) } +// Labels temporarily attaches one or more label names to the logger for the next log entry. +// Labels are typically used for metrics, benchmarking, tracing, or categorizing logs in a structured way. +// +// The labels are stored atomically and intended to be short-lived, applying only to the next +// log operation (or until overwritten by a subsequent call to Labels). Multiple labels can +// be provided as separate string arguments. +// +// Example usage: +// +// logger := New("app").Enable() +// +// // Add labels for a specific operation +// logger.Labels("load_users", "process_orders").Measure(func() { +// // ... perform work ... +// }, func() { +// // ... optional callback ... +// }) +func Labels(names ...string) *Logger { + return defaultLogger.Labels(names...) +} + +// Since creates a timer that will log the duration when completed +// If startTime is provided, uses that as the start time; otherwise uses time.Now() +// +// defer logger.Since().Info("request") // Auto-start +// logger.Since(start).Info("request") // Manual timing +// logger.Since().If(debug).Debug("timing") // Conditional +func Since(start ...time.Time) *SinceBuilder { + return defaultLogger.Since(start...) +} + // Benchmark logs the duration since a start time at Info level using the default logger. // It calculates the time elapsed since the provided start time and logs it with "start", // "end", and "duration" fields. Thread-safe via the Logger’s mutex. @@ -586,8 +615,8 @@ func Dbg(any ...interface{}) { // Example: // // ll.Dump([]byte{0x41, 0x42}) // Outputs hex/ASCII dump -func Dump(any interface{}) { - defaultLogger.Dump(any) +func Dump(values ...interface{}) { + defaultLogger.Dump(values...) } // Enabled returns whether the default logger is enabled for logging. @@ -672,3 +701,7 @@ func Apply(opts ...Option) *Logger { return defaultLogger.Apply(opts...) } + +func Toggle(v bool) *Logger { + return defaultLogger.Toggle(v) +} diff --git a/vendor/github.com/olekukonko/ll/inspector.go b/vendor/github.com/olekukonko/ll/inspector.go index fb6d690190..b816ac5c26 100644 --- a/vendor/github.com/olekukonko/ll/inspector.go +++ b/vendor/github.com/olekukonko/ll/inspector.go @@ -31,7 +31,7 @@ func NewInspector(logger *Logger) *Inspector { // Example usage within a Logger method: // // o := NewInspector(l) -// o.Log(2, someStruct) // Logs JSON representation with caller info +// o.Log(2, someStruct) func (o *Inspector) Log(skip int, values ...interface{}) { // Skip if logger is suspended or Info level is disabled if o.logger.suspend.Load() || !o.logger.shouldLog(lx.LevelInfo) { @@ -74,13 +74,13 @@ func (o *Inspector) Log(skip int, values ...interface{}) { } if err != nil { - o.logger.log(lx.LevelError, lx.ClassText, fmt.Sprintf("Inspector: JSON encoding error: %v", err), nil, false) + o.logger.log(lx.LevelError, lx.ClassInspect, fmt.Sprintf("Inspector: JSON encoding error: %v", err), nil, false) continue } // Construct log message with file, line, and JSON data - msg := fmt.Sprintf("[%s:%d] INSPECT: %s", shortFile, line, string(jsonData)) - o.logger.log(lx.LevelInfo, lx.ClassText, msg, nil, false) + msg := fmt.Sprintf("[%s:%d] %s", shortFile, line, string(jsonData)) + o.logger.log(lx.LevelInfo, lx.ClassInspect, msg, nil, false) } } diff --git a/vendor/github.com/olekukonko/ll/lh/buffered.go b/vendor/github.com/olekukonko/ll/lh/buffered.go index 0fc8c14d7f..9c388b0458 100644 --- a/vendor/github.com/olekukonko/ll/lh/buffered.go +++ b/vendor/github.com/olekukonko/ll/lh/buffered.go @@ -17,6 +17,7 @@ type Buffering struct { FlushInterval time.Duration // Maximum time between flushes (default: 10s) MaxBuffer int // Maximum buffer size before applying backpressure (default: 1000) OnOverflow func(int) // Called when buffer reaches MaxBuffer (default: logs warning) + ErrorOutput io.Writer // Destination for internal errors like flush failures (default: os.Stderr) } // BufferingOpt configures Buffered handler. @@ -66,6 +67,18 @@ func WithOverflowHandler(fn func(int)) BufferingOpt { } } +// WithErrorOutput sets the destination for internal errors (e.g., downstream handler failures). +// Defaults to os.Stderr if not set. +// Example: +// +// // Redirect internal errors to a file or discard them +// handler := NewBuffered(textHandler, WithErrorOutput(os.Stdout)) +func WithErrorOutput(w io.Writer) BufferingOpt { + return func(c *Buffering) { + c.ErrorOutput = w + } +} + // Buffered wraps any Handler to provide buffering capabilities. // It buffers log entries in a channel and flushes them based on batch size, time interval, or explicit flush. // The generic type H ensures compatibility with any lx.Handler implementation. @@ -93,7 +106,8 @@ func NewBuffered[H lx.Handler](handler H, opts ...BufferingOpt) *Buffered[H] { BatchSize: 100, // Default: flush every 100 entries FlushInterval: 10 * time.Second, // Default: flush every 10 seconds MaxBuffer: 1000, // Default: max 1000 entries in buffer - OnOverflow: func(count int) { // Default: log overflow to io.Discard + ErrorOutput: os.Stderr, // Default: report errors to stderr + OnOverflow: func(count int) { // Default: log overflow to io.Discard (silent by default for overflow) fmt.Fprintf(io.Discard, "log buffer overflow: %d entries\n", count) }, } @@ -113,6 +127,9 @@ func NewBuffered[H lx.Handler](handler H, opts ...BufferingOpt) *Buffered[H] { if config.FlushInterval <= 0 { config.FlushInterval = 10 * time.Second // Minimum flush interval is 10s } + if config.ErrorOutput == nil { + config.ErrorOutput = os.Stderr + } // Initialize Buffered handler b := &Buffered[H]{ @@ -173,18 +190,25 @@ func (b *Buffered[H]) Flush() { // Close flushes any remaining entries and stops the worker. // It ensures shutdown is performed only once and waits for the worker to finish. +// If the underlying handler implements a Close() error method, it will be called to release resources. // Thread-safe via sync.Once and WaitGroup. -// Returns nil as it does not produce errors. +// Returns any error from the underlying handler's Close, or nil. // Example: // // buffered.Close() // Flushes entries and stops worker func (b *Buffered[H]) Close() error { + var closeErr error b.shutdownOnce.Do(func() { close(b.shutdown) // Signal worker to shut down b.wg.Wait() // Wait for worker to finish runtime.SetFinalizer(b, nil) // Remove finalizer + + // Check if underlying handler has a Close method and call it + if closer, ok := any(b.handler).(interface{ Close() error }); ok { + closeErr = closer.Close() + } }) - return nil + return closeErr } // Final ensures remaining entries are flushed during garbage collection. @@ -246,7 +270,7 @@ func (b *Buffered[H]) worker() { } // flushBatch processes a batch of entries through the wrapped handler. -// It writes each entry to the underlying handler, logging any errors to stderr. +// It writes each entry to the underlying handler, logging any errors to the configured ErrorOutput. // Example (internal usage): // // b.flushBatch([]*lx.Entry{entry1, entry2}) @@ -254,14 +278,16 @@ func (b *Buffered[H]) flushBatch(batch []*lx.Entry) { for _, entry := range batch { // Process each entry through the handler if err := b.handler.Handle(entry); err != nil { - fmt.Fprintf(os.Stderr, "log flush error: %v\n", err) // Log errors to stderr + if b.config.ErrorOutput != nil { + fmt.Fprintf(b.config.ErrorOutput, "log flush error: %v\n", err) + } } } } // drainRemaining processes any remaining entries in the channel. // It flushes all entries from the entries channel to the underlying handler, -// logging any errors to stderr. Used during flush or shutdown. +// logging any errors to the configured ErrorOutput. Used during flush or shutdown. // Example (internal usage): // // b.drainRemaining() // Flushes all pending entries @@ -270,7 +296,9 @@ func (b *Buffered[H]) drainRemaining() { select { case entry := <-b.entries: // Process next entry if err := b.handler.Handle(entry); err != nil { - fmt.Fprintf(os.Stderr, "log drain error: %v\n", err) // Log errors to stderr + if b.config.ErrorOutput != nil { + fmt.Fprintf(b.config.ErrorOutput, "log drain error: %v\n", err) + } } default: // Exit when channel is empty return diff --git a/vendor/github.com/olekukonko/ll/lh/colorized.go b/vendor/github.com/olekukonko/ll/lh/colorized.go index fcc0c9ec8e..05285002fb 100644 --- a/vendor/github.com/olekukonko/ll/lh/colorized.go +++ b/vendor/github.com/olekukonko/ll/lh/colorized.go @@ -1,10 +1,12 @@ package lh import ( + "bytes" "fmt" "io" "os" - "sort" + "runtime" + "strconv" "strings" "sync" "time" @@ -12,15 +14,23 @@ import ( "github.com/olekukonko/ll/lx" ) +// ColorIntensity defines the intensity level for ANSI colors +type ColorIntensity int + +const ( + IntensityNormal ColorIntensity = iota + IntensityBright + IntensityPastel + IntensityVibrant +) + // Palette defines ANSI color codes for various log components. -// It specifies colors for headers, goroutines, functions, paths, stack traces, and log levels, -// used by ColorizedHandler to format log output with color. type Palette struct { Header string // Color for stack trace header and dump separators Goroutine string // Color for goroutine lines in stack traces Func string // Color for function names in stack traces Path string // Color for file paths in stack traces - FileLine string // Color for file line numbers (not used in provided code) + FileLine string // Color for file line numbers Reset string // Reset code to clear color formatting Pos string // Color for position in hex dumps Hex string // Color for hex values in dumps @@ -31,135 +41,350 @@ type Palette struct { Error string // Color for Error level messages Fatal string // Color for Fatal level messages Title string // Color for dump titles (BEGIN/END separators) + + // Field type colors + Key string // Color for field keys + Number string // Color for numbers + String string // Color for strings + Bool string // Color for booleans + Time string // Color for timestamps/durations + Nil string // Color for nil values + Default string // Default color for unknown types + + // JSON and Inspect specific colors + JSONKey string // Color for JSON keys + JSONString string // Color for JSON string values + JSONNumber string // Color for JSON number values + JSONBool string // Color for JSON boolean values + JSONNull string // Color for JSON null values + JSONBrace string // Color for JSON braces and brackets + InspectKey string // Color for inspect keys + InspectValue string // Color for inspect values + InspectMeta string // Color for inspect metadata (annotations) } // darkPalette defines colors optimized for dark terminal backgrounds. -// It uses bright, contrasting colors for readability on dark backgrounds. var darkPalette = Palette{ - Header: "\033[1;31m", // Bold red for headers - Goroutine: "\033[1;36m", // Bold cyan for goroutines - Func: "\033[97m", // Bright white for functions - Path: "\033[38;5;245m", // Light gray for paths - FileLine: "\033[38;5;111m", // Muted light blue (unused) - Reset: "\033[0m", // Reset color formatting + Header: "\033[1;38;5;203m", // Brighter red + Goroutine: "\033[1;38;5;51m", // Bright cyan + Func: "\033[1;97m", // Bright white + Path: "\033[38;5;110m", // Brighter gray-blue + FileLine: "\033[38;5;117m", // Bright blue + Reset: "\033[0m", + Title: "\033[38;5;245m", + Pos: "\033[38;5;117m", + Hex: "\033[38;5;156m", + Ascii: "\033[38;5;224m", + Debug: "\033[36m", + Info: "\033[32m", + Warn: "\033[33m", + Error: "\033[31m", + Fatal: "\033[1;31m", - Title: "\033[38;5;245m", // Light gray for dump titles - Pos: "\033[38;5;117m", // Light blue for dump positions - Hex: "\033[38;5;156m", // Light green for hex values - Ascii: "\033[38;5;224m", // Light pink for ASCII values + // Field type colors - made brighter for dark backgrounds + Key: "\033[38;5;117m", // Brighter blue + Number: "\033[38;5;141m", // Brighter purple + String: "\033[38;5;223m", // Brighter yellow/orange + Bool: "\033[38;5;85m", // Brighter green + Time: "\033[38;5;110m", // Brighter cyan-blue + Nil: "\033[38;5;243m", // Slightly brighter gray + Default: "\033[38;5;250m", // Brighter gray - Debug: "\033[36m", // Cyan for Debug level - Info: "\033[32m", // Green for Info level - Warn: "\033[33m", // Yellow for Warn level - Error: "\033[31m", // Standard red - Fatal: "\033[1;31m", // Bold red - stands out more + // JSON and Inspect colors + JSONKey: "\033[38;5;117m", + JSONString: "\033[38;5;223m", + JSONNumber: "\033[38;5;141m", + JSONBool: "\033[38;5;85m", + JSONNull: "\033[38;5;243m", + JSONBrace: "\033[38;5;245m", + InspectKey: "\033[38;5;117m", + InspectValue: "\033[38;5;223m", + InspectMeta: "\033[38;5;243m", } // lightPalette defines colors optimized for light terminal backgrounds. -// It uses darker colors for better contrast on light backgrounds. var lightPalette = Palette{ - Header: "\033[1;31m", // Same red for headers - Goroutine: "\033[34m", // Blue (darker for light bg) - Func: "\033[30m", // Black text for functions - Path: "\033[90m", // Dark gray for paths - FileLine: "\033[94m", // Blue for file lines (unused) - Reset: "\033[0m", // Reset color formatting + Header: "\033[1;31m", + Goroutine: "\033[34m", + Func: "\033[30m", + Path: "\033[90m", + FileLine: "\033[94m", + Reset: "\033[0m", + Title: "\033[38;5;245m", + Pos: "\033[38;5;117m", + Hex: "\033[38;5;156m", + Ascii: "\033[38;5;224m", + Debug: "\033[36m", + Info: "\033[32m", + Warn: "\033[33m", + Error: "\033[31m", + Fatal: "\033[1;31m", - Title: "\033[38;5;245m", // Light gray for dump titles - Pos: "\033[38;5;117m", // Light blue for dump positions - Hex: "\033[38;5;156m", // Light green for hex values - Ascii: "\033[38;5;224m", // Light pink for ASCII values + Key: "\033[34m", + Number: "\033[35m", + String: "\033[38;5;94m", + Bool: "\033[32m", + Time: "\033[38;5;24m", + Nil: "\033[38;5;240m", + Default: "\033[30m", - Debug: "\033[36m", // Cyan for Debug level - Info: "\033[32m", // Green for Info level - Warn: "\033[33m", // Yellow for Warn level - Error: "\033[31m", // Standard red - Fatal: "\033[1;31m", // Bold red - stands out more + JSONKey: "\033[1;34m", + JSONString: "\033[1;33m", + JSONNumber: "\033[1;35m", + JSONBool: "\033[1;32m", + JSONNull: "\033[1;37m", + JSONBrace: "\033[1;37m", + InspectKey: "\033[1;34m", + InspectValue: "\033[1;33m", + InspectMeta: "\033[1;37m", +} + +// brightPalette defines vibrant, high-contrast colors +var brightPalette = Palette{ + Header: "\033[1;91m", + Goroutine: "\033[1;96m", + Func: "\033[1;97m", + Path: "\033[38;5;250m", + FileLine: "\033[38;5;117m", + Reset: "\033[0m", + Title: "\033[1;37m", + Pos: "\033[1;33m", + Hex: "\033[1;32m", + Ascii: "\033[1;35m", + Debug: "\033[1;36m", + Info: "\033[1;32m", + Warn: "\033[1;33m", + Error: "\033[1;31m", + Fatal: "\033[1;91m", + + Key: "\033[1;34m", + Number: "\033[1;35m", + String: "\033[1;33m", + Bool: "\033[1;32m", + Time: "\033[1;36m", + Nil: "\033[1;37m", + Default: "\033[1;37m", + + JSONKey: "\033[1;34m", + JSONString: "\033[1;33m", + JSONNumber: "\033[1;35m", + JSONBool: "\033[1;32m", + JSONNull: "\033[1;37m", + JSONBrace: "\033[1;37m", + InspectKey: "\033[1;34m", + InspectValue: "\033[1;33m", + InspectMeta: "\033[1;37m", +} + +// pastelPalette defines soft, pastel colors +var pastelPalette = Palette{ + Header: "\033[38;5;211m", + Goroutine: "\033[38;5;153m", + Func: "\033[38;5;255m", + Path: "\033[38;5;248m", + FileLine: "\033[38;5;111m", + Reset: "\033[0m", + Title: "\033[38;5;248m", + Pos: "\033[38;5;153m", + Hex: "\033[38;5;158m", + Ascii: "\033[38;5;218m", + Debug: "\033[38;5;122m", + Info: "\033[38;5;120m", + Warn: "\033[38;5;221m", + Error: "\033[38;5;211m", + Fatal: "\033[38;5;204m", + + Key: "\033[38;5;153m", + Number: "\033[38;5;183m", + String: "\033[38;5;223m", + Bool: "\033[38;5;120m", + Time: "\033[38;5;117m", + Nil: "\033[38;5;247m", + Default: "\033[38;5;250m", + + JSONKey: "\033[38;5;153m", + JSONString: "\033[38;5;223m", + JSONNumber: "\033[38;5;183m", + JSONBool: "\033[38;5;120m", + JSONNull: "\033[38;5;247m", + JSONBrace: "\033[38;5;247m", + InspectKey: "\033[38;5;153m", + InspectValue: "\033[38;5;223m", + InspectMeta: "\033[38;5;247m", +} + +// vibrantPalette defines highly saturated, eye-catching colors +var vibrantPalette = Palette{ + Header: "\033[38;5;196m", + Goroutine: "\033[38;5;51m", + Func: "\033[38;5;15m", + Path: "\033[38;5;244m", + FileLine: "\033[38;5;75m", + Reset: "\033[0m", + Title: "\033[38;5;244m", + Pos: "\033[38;5;51m", + Hex: "\033[38;5;46m", + Ascii: "\033[38;5;201m", + Debug: "\033[38;5;51m", + Info: "\033[38;5;46m", + Warn: "\033[38;5;226m", + Error: "\033[38;5;196m", + Fatal: "\033[1;38;5;196m", + + Key: "\033[38;5;33m", + Number: "\033[38;5;129m", + String: "\033[38;5;214m", + Bool: "\033[38;5;46m", + Time: "\033[38;5;75m", + Nil: "\033[38;5;242m", + Default: "\033[38;5;15m", + + JSONKey: "\033[38;5;33m", + JSONString: "\033[38;5;214m", + JSONNumber: "\033[38;5;129m", + JSONBool: "\033[38;5;46m", + JSONNull: "\033[38;5;242m", + JSONBrace: "\033[38;5;242m", + InspectKey: "\033[38;5;33m", + InspectValue: "\033[38;5;214m", + InspectMeta: "\033[38;5;242m", +} + +// noColorPalette defines a palette with empty strings for environments without color support +var noColorPalette = Palette{ + Header: "", Goroutine: "", Func: "", Path: "", FileLine: "", Reset: "", + Title: "", Pos: "", Hex: "", Ascii: "", Debug: "", Info: "", Warn: "", Error: "", Fatal: "", + Key: "", Number: "", String: "", Bool: "", Time: "", Nil: "", Default: "", + JSONKey: "", JSONString: "", JSONNumber: "", JSONBool: "", JSONNull: "", JSONBrace: "", + InspectKey: "", InspectValue: "", InspectMeta: "", +} + +// colorBufPool is a pool of bytes.Buffer instances to reduce allocations +var colorBufPool = sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, } // ColorizedHandler is a handler that outputs log entries with ANSI color codes. -// It formats log entries with colored namespace, level, message, fields, and stack traces, -// writing the result to the provided writer. -// Thread-safe if the underlying writer is thread-safe. type ColorizedHandler struct { - w io.Writer // Destination for colored log output - palette Palette // Color scheme for formatting - showTime bool // Whether to display timestamps - timeFormat string // Format for timestamps (defaults to time.RFC3339) - mu sync.Mutex + writer io.Writer + palette Palette + showTime bool + timeFormat string + mu sync.Mutex + noColor bool // Whether to disable colors entirely + intensity ColorIntensity // Color intensity level + colorFields bool // Whether to colorize fields (default: true) } // ColorOption defines a configuration function for ColorizedHandler. -// It allows customization of the handler, such as setting the color palette. type ColorOption func(*ColorizedHandler) // WithColorPallet sets the color palette for the ColorizedHandler. -// It allows specifying a custom Palette for dark or light terminal backgrounds. -// Example: -// -// handler := NewColorizedHandler(os.Stdout, WithColorPallet(lightPalette)) func WithColorPallet(pallet Palette) ColorOption { return func(c *ColorizedHandler) { c.palette = pallet } } -// NewColorizedHandler creates a new ColorizedHandler writing to the specified writer. -// It initializes the handler with a detected or specified color palette and applies -// optional configuration functions. +// WithColorNone disables all color output. +func WithColorNone() ColorOption { + return func(c *ColorizedHandler) { + c.noColor = true + c.colorFields = false // Also disable field coloring + } +} + +// WithColorField enables or disables field coloring specifically. +// This is useful for performance optimization or when field colors are too much. // Example: // -// handler := NewColorizedHandler(os.Stdout) -// logger := ll.New("app").Enable().Handler(handler) -// logger.Info("Test") // Output: [app] : Test +// handler := NewColorizedHandler(os.Stdout, WithColorField(false)) // Disable field coloring only +func WithColorField(enable bool) ColorOption { + return func(c *ColorizedHandler) { + c.colorFields = enable + } +} + +// WithColorShowTime enables or disables the display of timestamps. +func WithColorShowTime(show bool) ColorOption { + return func(c *ColorizedHandler) { + c.showTime = show + } +} + +// WithColorIntensity sets the color intensity for the ColorizedHandler. +func WithColorIntensity(intensity ColorIntensity) ColorOption { + return func(c *ColorizedHandler) { + c.intensity = intensity + } +} + +// WithColorTheme configures the ColorizedHandler to use a specific color theme based on the provided theme name. +func WithColorTheme(theme string) ColorOption { + return func(c *ColorizedHandler) { + switch strings.ToLower(theme) { + case "light": + c.palette = lightPalette + case "dark": + c.palette = darkPalette + case "bright": + c.palette = brightPalette + case "pastel": + c.palette = pastelPalette + case "vibrant": + c.palette = vibrantPalette + } + } +} + +// NewColorizedHandler creates a new ColorizedHandler writing to the specified writer. func NewColorizedHandler(w io.Writer, opts ...ColorOption) *ColorizedHandler { - // Initialize with writer - c := &ColorizedHandler{w: w, - showTime: false, - timeFormat: time.RFC3339, + c := &ColorizedHandler{ + writer: w, + showTime: false, + timeFormat: time.RFC3339, + noColor: false, + intensity: IntensityNormal, + colorFields: true, // Default: enable field coloring } - // Apply configuration options for _, opt := range opts { opt(c) } - // Detect palette if not set + c.palette = c.detectPalette() return c } -// Handle processes a log entry and writes it with ANSI color codes. -// It delegates to specialized methods based on the entry's class (Dump, Raw, or regular). -// Returns an error if writing to the underlying writer fails. -// Thread-safe if the writer is thread-safe. -// Example: -// -// handler.Handle(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes colored output -func (h *ColorizedHandler) Handle(e *lx.Entry) error { +func (h *ColorizedHandler) Output(w io.Writer) { + h.mu.Lock() + defer h.mu.Unlock() + h.writer = w +} +// Handle processes a log entry and writes it with ANSI color codes. +func (h *ColorizedHandler) Handle(e *lx.Entry) error { h.mu.Lock() defer h.mu.Unlock() switch e.Class { case lx.ClassDump: - // Handle hex dump entries return h.handleDumpOutput(e) + case lx.ClassJSON: + return h.handleJSONOutput(e) + case lx.ClassInspect: + return h.handleInspectOutput(e) case lx.ClassRaw: - // Write raw entries directly - _, err := h.w.Write([]byte(e.Message)) + _, err := h.writer.Write([]byte(e.Message)) return err default: - // Handle standard log entries return h.handleRegularOutput(e) } } -// Timestamped enables or disables timestamp display and optionally sets a custom time format. -// If format is empty, defaults to RFC3339. -// Example: -// -// handler := NewColorizedHandler(os.Stdout).Timestamped(true, time.StampMilli) -// // Output: Jan 02 15:04:05.000 [app] INFO: Test +// Timestamped enables or disables timestamp display. func (h *ColorizedHandler) Timestamped(enable bool, format ...string) { h.showTime = enable if len(format) > 0 && format[0] != "" { @@ -168,53 +393,291 @@ func (h *ColorizedHandler) Timestamped(enable bool, format ...string) { } // handleRegularOutput handles normal log entries. -// It formats the entry with colored namespace, level, message, fields, and stack trace (if present), -// writing the result to the handler's writer. -// Returns an error if writing fails. -// Example (internal usage): -// -// h.handleRegularOutput(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes colored output func (h *ColorizedHandler) handleRegularOutput(e *lx.Entry) error { - var builder strings.Builder // Buffer for building formatted output + buf := colorBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer colorBufPool.Put(buf) - // Add timestamp if enabled if h.showTime { - builder.WriteString(e.Timestamp.Format(h.timeFormat)) - builder.WriteString(lx.Space) + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Space) } - // Format namespace with colors - h.formatNamespace(&builder, e) + h.formatNamespace(buf, e) + h.formatLevel(buf, e) + buf.WriteString(e.Message) + h.formatFields(buf, e) - // Format level with color based on severity - h.formatLevel(&builder, e) - - // Add message and fields - builder.WriteString(e.Message) - h.formatFields(&builder, e) - - // fmt.Println("------------>", len(e.Stack)) - // Format stack trace if present if len(e.Stack) > 0 { - h.formatStack(&builder, e.Stack) + h.formatStack(buf, e.Stack) } - // Append newline for non-None levels if e.Level != lx.LevelNone { - builder.WriteString(lx.Newline) + buf.WriteString(lx.Newline) } - // Write formatted output to writer - _, err := h.w.Write([]byte(builder.String())) + _, err := h.writer.Write(buf.Bytes()) return err } +// handleJSONOutput handles JSON log entries. +func (h *ColorizedHandler) handleJSONOutput(e *lx.Entry) error { + buf := colorBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer colorBufPool.Put(buf) + + if h.showTime { + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Newline) + } + + if e.Namespace != "" { + h.formatNamespace(buf, e) + h.formatLevel(buf, e) + } + + h.colorizeJSON(buf, e.Message) + buf.WriteString(lx.Newline) + + _, err := h.writer.Write(buf.Bytes()) + return err +} + +// handleInspectOutput handles inspect log entries. +func (h *ColorizedHandler) handleInspectOutput(e *lx.Entry) error { + buf := colorBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer colorBufPool.Put(buf) + + if h.showTime { + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Space) + } + + h.formatNamespace(buf, e) + h.formatLevel(buf, e) + h.colorizeInspect(buf, e.Message) + buf.WriteString(lx.Newline) + + _, err := h.writer.Write(buf.Bytes()) + return err +} + +// colorizeJSON applies syntax highlighting to JSON strings without changing formatting +func (h *ColorizedHandler) colorizeJSON(b *bytes.Buffer, jsonStr string) { + inString := false + escapeNext := false + + for i := 0; i < len(jsonStr); i++ { + ch := jsonStr[i] + + if escapeNext { + b.WriteByte(ch) + escapeNext = false + continue + } + + switch ch { + case '\\': + escapeNext = true + if inString { + b.WriteString(h.palette.JSONString) + } + b.WriteByte(ch) + + case '"': + if inString { + // End of string + b.WriteString(h.palette.JSONString) + b.WriteByte(ch) + b.WriteString(h.palette.Reset) + inString = false + } else { + // Start of string + inString = true + b.WriteString(h.palette.JSONString) + b.WriteByte(ch) + } + + case ':': + if !inString { + b.WriteString(h.palette.JSONBrace) + b.WriteByte(ch) + b.WriteString(h.palette.Reset) + } else { + b.WriteByte(ch) + } + + case '{', '}', '[', ']', ',': + if !inString { + b.WriteString(h.palette.JSONBrace) + b.WriteByte(ch) + b.WriteString(h.palette.Reset) + } else { + b.WriteByte(ch) + } + + default: + if !inString { + // Check for numbers, booleans, null + remaining := jsonStr[i:] + + // Check for null + if len(remaining) >= 4 && strings.HasPrefix(remaining, "null") { + b.WriteString(h.palette.JSONNull) + b.WriteString("null") + b.WriteString(h.palette.Reset) + i += 3 // Skip "null" + } else if len(remaining) >= 4 && strings.HasPrefix(remaining, "true") { + b.WriteString(h.palette.JSONBool) + b.WriteString("true") + b.WriteString(h.palette.Reset) + i += 3 // Skip "true" + } else if len(remaining) >= 5 && strings.HasPrefix(remaining, "false") { + b.WriteString(h.palette.JSONBool) + b.WriteString("false") + b.WriteString(h.palette.Reset) + i += 4 // Skip "false" + } else if (ch >= '0' && ch <= '9') || ch == '-' || ch == '.' { + b.WriteString(h.palette.JSONNumber) + b.WriteByte(ch) + // Continue writing digits + for j := i + 1; j < len(jsonStr); j++ { + nextCh := jsonStr[j] + if (nextCh >= '0' && nextCh <= '9') || nextCh == '.' || nextCh == 'e' || nextCh == 'E' || nextCh == '+' || nextCh == '-' { + b.WriteByte(nextCh) + i = j + } else { + break + } + } + b.WriteString(h.palette.Reset) + } else if ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r' { + // Preserve whitespace exactly as is + b.WriteByte(ch) + } else { + // Unexpected character outside string - preserve it + b.WriteByte(ch) + } + } else { + // Inside string + b.WriteByte(ch) + } + } + } +} + +// colorizeInspect applies syntax highlighting to inspect output +func (h *ColorizedHandler) colorizeInspect(b *bytes.Buffer, inspectStr string) { + lines := strings.Split(inspectStr, "\n") + + for lineIdx, line := range lines { + if lineIdx > 0 { + b.WriteString("\n") + } + + trimmed := strings.TrimSpace(line) + if trimmed == "" { + b.WriteString(line) + continue + } + + // For inspect output, we'll do simple line-based coloring + // This preserves the original formatting + inString := false + escapeNext := false + + for i := 0; i < len(line); i++ { + ch := line[i] + + if escapeNext { + b.WriteByte(ch) + escapeNext = false + continue + } + + if ch == '\\' { + escapeNext = true + b.WriteByte(ch) + continue + } + + if ch == '"' { + inString = !inString + if inString { + // Check if this is a metadata key + if i+1 < len(line) && line[i+1] == '(' { + b.WriteString(h.palette.InspectMeta) + } else if i+2 < len(line) && line[i+1] == '*' && line[i+2] == '(' { + b.WriteString(h.palette.InspectMeta) + } else { + b.WriteString(h.palette.InspectKey) + } + } + b.WriteByte(ch) + if !inString { + b.WriteString(h.palette.Reset) + } + continue + } + + if inString { + // Inside a string key or value + b.WriteByte(ch) + } else { + // Outside strings + if ch == ':' { + b.WriteString(h.palette.JSONBrace) + b.WriteByte(ch) + b.WriteString(h.palette.Reset) + } else if ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' { + b.WriteString(h.palette.JSONBrace) + b.WriteByte(ch) + b.WriteString(h.palette.Reset) + } else { + // Check for numbers, booleans, null outside strings + remaining := line[i:] + + if len(remaining) >= 4 && strings.HasPrefix(remaining, "null") { + b.WriteString(h.palette.JSONNull) + b.WriteString("null") + b.WriteString(h.palette.Reset) + i += 3 + } else if len(remaining) >= 4 && strings.HasPrefix(remaining, "true") { + b.WriteString(h.palette.JSONBool) + b.WriteString("true") + b.WriteString(h.palette.Reset) + i += 3 + } else if len(remaining) >= 5 && strings.HasPrefix(remaining, "false") { + b.WriteString(h.palette.JSONBool) + b.WriteString("false") + b.WriteString(h.palette.Reset) + i += 4 + } else if (ch >= '0' && ch <= '9') || ch == '-' { + b.WriteString(h.palette.InspectValue) + b.WriteByte(ch) + // Continue writing digits + for j := i + 1; j < len(line); j++ { + nextCh := line[j] + if (nextCh >= '0' && nextCh <= '9') || nextCh == '.' { + b.WriteByte(nextCh) + i = j + } else { + break + } + } + b.WriteString(h.palette.Reset) + } else { + b.WriteByte(ch) + } + } + } + } + } +} + // formatNamespace formats the namespace with ANSI color codes. -// It supports FlatPath ([parent/child]) and NestedPath ([parent]→[child]) styles. -// Example (internal usage): -// -// h.formatNamespace(&builder, &lx.Entry{Namespace: "parent/child", Style: lx.FlatPath}) // Writes "[parent/child]: " -func (h *ColorizedHandler) formatNamespace(b *strings.Builder, e *lx.Entry) { +func (h *ColorizedHandler) formatNamespace(b *bytes.Buffer, e *lx.Entry) { if e.Namespace == "" { return } @@ -222,7 +685,6 @@ func (h *ColorizedHandler) formatNamespace(b *strings.Builder, e *lx.Entry) { b.WriteString(lx.LeftBracket) switch e.Style { case lx.NestedPath: - // Split namespace and format as [parent]→[child] parts := strings.Split(e.Namespace, lx.Slash) for i, part := range parts { b.WriteString(part) @@ -232,8 +694,7 @@ func (h *ColorizedHandler) formatNamespace(b *strings.Builder, e *lx.Entry) { b.WriteString(lx.LeftBracket) } } - default: // FlatPath - // Format as [parent/child] + default: b.WriteString(e.Namespace) b.WriteString(lx.RightBracket) } @@ -242,65 +703,151 @@ func (h *ColorizedHandler) formatNamespace(b *strings.Builder, e *lx.Entry) { } // formatLevel formats the log level with ANSI color codes. -// It applies a color based on the level (Debug, Info, Warn, Error) and resets afterward. -// Example (internal usage): -// -// h.formatLevel(&builder, &lx.Entry{Level: lx.LevelInfo}) // Writes "INFO: " -func (h *ColorizedHandler) formatLevel(b *strings.Builder, e *lx.Entry) { - // Map levels to colors +func (h *ColorizedHandler) formatLevel(b *bytes.Buffer, e *lx.Entry) { color := map[lx.LevelType]string{ - lx.LevelDebug: h.palette.Debug, // Cyan - lx.LevelInfo: h.palette.Info, // Green - lx.LevelWarn: h.palette.Warn, // Yellow - lx.LevelError: h.palette.Error, // Red - lx.LevelFatal: h.palette.Fatal, // Bold Red + lx.LevelDebug: h.palette.Debug, + lx.LevelInfo: h.palette.Info, + lx.LevelWarn: h.palette.Warn, + lx.LevelError: h.palette.Error, + lx.LevelFatal: h.palette.Fatal, }[e.Level] b.WriteString(color) - b.WriteString(e.Level.String()) + //b.WriteString(rightPad(e.Level.Name(e.Class), 8)) + b.WriteString(e.Level.Name(e.Class)) b.WriteString(h.palette.Reset) + // b.WriteString(lx.Space) b.WriteString(lx.Colon) b.WriteString(lx.Space) } // formatFields formats the log entry's fields in sorted order. -// It writes fields as [key=value key=value], with no additional coloring. -// Example (internal usage): -// -// h.formatFields(&builder, &lx.Entry{Fields: map[string]interface{}{"key": "value"}}) // Writes " [key=value]" -func (h *ColorizedHandler) formatFields(b *strings.Builder, e *lx.Entry) { +func (h *ColorizedHandler) formatFields(b *bytes.Buffer, e *lx.Entry) { if len(e.Fields) == 0 { return } - // Collect and sort field keys - var keys []string - for k := range e.Fields { - keys = append(keys, k) - } - sort.Strings(keys) - b.WriteString(lx.Space) b.WriteString(lx.LeftBracket) - // Format fields as key=value - for i, k := range keys { + + for i, pair := range e.Fields { if i > 0 { b.WriteString(lx.Space) } - b.WriteString(k) - b.WriteString("=") - b.WriteString(fmt.Sprint(e.Fields[k])) + + if h.colorFields { + // Color the key + b.WriteString(h.palette.Key) + b.WriteString(pair.Key) + b.WriteString(h.palette.Reset) + b.WriteString("=") + + // Format value with type-based coloring + h.formatFieldValue(b, pair.Value) + } else { + // No field coloring - just write plain text + b.WriteString(pair.Key) + b.WriteString("=") + fmt.Fprint(b, pair.Value) + } } + b.WriteString(lx.RightBracket) } +// formatFieldValue formats a field value with type-based ANSI color codes. +func (h *ColorizedHandler) formatFieldValue(b *bytes.Buffer, value interface{}) { + // If field coloring is disabled, just write the value + if !h.colorFields { + fmt.Fprint(b, value) + return + } + + switch v := value.(type) { + case time.Time: + b.WriteString(h.palette.Time) + b.WriteString(v.Format("2006-01-02 15:04:05")) + b.WriteString(h.palette.Reset) + + case time.Duration: + b.WriteString(h.palette.Time) + h.formatDuration(b, v) + b.WriteString(h.palette.Reset) + + case error: + b.WriteString(h.palette.Error) + b.WriteString(`"`) + b.WriteString(v.Error()) + b.WriteString(`"`) + b.WriteString(h.palette.Reset) + + case int, int8, int16, int32, int64: + b.WriteString(h.palette.Number) + fmt.Fprint(b, v) + b.WriteString(h.palette.Reset) + + case uint, uint8, uint16, uint32, uint64: + b.WriteString(h.palette.Number) + fmt.Fprint(b, v) + b.WriteString(h.palette.Reset) + + case float32, float64: + b.WriteString(h.palette.Number) + switch f := v.(type) { + case float32: + fmt.Fprintf(b, "%.6g", f) + case float64: + fmt.Fprintf(b, "%.6g", f) + } + b.WriteString(h.palette.Reset) + + case string: + b.WriteString(h.palette.String) + b.WriteString(`"`) + b.WriteString(v) + b.WriteString(`"`) + b.WriteString(h.palette.Reset) + + case bool: + b.WriteString(h.palette.Bool) + fmt.Fprint(b, v) + b.WriteString(h.palette.Reset) + + case nil: + b.WriteString(h.palette.Nil) + b.WriteString("nil") + b.WriteString(h.palette.Reset) + + default: + b.WriteString(h.palette.Default) + fmt.Fprint(b, v) + b.WriteString(h.palette.Reset) + } +} + +// formatDuration formats a duration in a human-readable way +func (h *ColorizedHandler) formatDuration(b *bytes.Buffer, d time.Duration) { + if d < time.Microsecond { + b.WriteString(d.String()) + } else if d < time.Millisecond { + fmt.Fprintf(b, "%.3fµs", float64(d)/float64(time.Microsecond)) + } else if d < time.Second { + fmt.Fprintf(b, "%.3fms", float64(d)/float64(time.Millisecond)) + } else if d < time.Minute { + fmt.Fprintf(b, "%.3fs", float64(d)/float64(time.Second)) + } else if d < time.Hour { + minutes := d / time.Minute + seconds := (d % time.Minute) / time.Second + fmt.Fprintf(b, "%dm%.3fs", minutes, float64(seconds)/float64(time.Second)) + } else { + hours := d / time.Hour + minutes := (d % time.Hour) / time.Minute + fmt.Fprintf(b, "%dh%dm", hours, minutes) + } +} + // formatStack formats a stack trace with ANSI color codes. -// It structures the stack trace with colored goroutine, function, and path segments, -// using indentation and separators for readability. -// Example (internal usage): -// -// h.formatStack(&builder, []byte("goroutine 1 [running]:\nmain.main()\n\tmain.go:10")) // Appends colored stack trace -func (h *ColorizedHandler) formatStack(b *strings.Builder, stack []byte) { +func (h *ColorizedHandler) formatStack(b *bytes.Buffer, stack []byte) { b.WriteString("\n") b.WriteString(h.palette.Header) b.WriteString("[stack]") @@ -312,14 +859,12 @@ func (h *ColorizedHandler) formatStack(b *strings.Builder, stack []byte) { return } - // Format goroutine line b.WriteString(" ┌─ ") b.WriteString(h.palette.Goroutine) b.WriteString(lines[0]) b.WriteString(h.palette.Reset) b.WriteString("\n") - // Pair function name and file path lines for i := 1; i < len(lines)-1; i += 2 { funcLine := strings.TrimSpace(lines[i]) pathLine := strings.TrimSpace(lines[i+1]) @@ -334,25 +879,21 @@ func (h *ColorizedHandler) formatStack(b *strings.Builder, stack []byte) { if pathLine != "" { b.WriteString(" │ ") - // Look for last "/" before ".go:" lastSlash := strings.LastIndex(pathLine, "/") goIndex := strings.Index(pathLine, ".go:") if lastSlash >= 0 && goIndex > lastSlash { - // Prefix path prefix := pathLine[:lastSlash+1] - // File and line (e.g., ll.go:698 +0x5c) suffix := pathLine[lastSlash+1:] b.WriteString(h.palette.Path) b.WriteString(prefix) b.WriteString(h.palette.Reset) - b.WriteString(h.palette.Path) // Use mainPath color for suffix + b.WriteString(h.palette.Path) b.WriteString(suffix) b.WriteString(h.palette.Reset) } else { - // Fallback: whole line is gray b.WriteString(h.palette.Path) b.WriteString(pathLine) b.WriteString(h.palette.Reset) @@ -362,7 +903,6 @@ func (h *ColorizedHandler) formatStack(b *strings.Builder, stack []byte) { } } - // Handle any remaining unpaired line if len(lines)%2 == 0 && strings.TrimSpace(lines[len(lines)-1]) != "" { b.WriteString(" │ ") b.WriteString(h.palette.Func) @@ -375,110 +915,149 @@ func (h *ColorizedHandler) formatStack(b *strings.Builder, stack []byte) { } // handleDumpOutput formats hex dump output with ANSI color codes. -// It applies colors to position, hex, ASCII, and title components of the dump, -// wrapping the output with colored BEGIN/END separators. -// Returns an error if writing fails. -// Example (internal usage): -// -// h.handleDumpOutput(&lx.Entry{Class: lx.ClassDump, Message: "pos 00 hex: 61 62 'ab'"}) // Writes colored dump func (h *ColorizedHandler) handleDumpOutput(e *lx.Entry) error { - var builder strings.Builder + buf := colorBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer colorBufPool.Put(buf) - // Add timestamp if enabled if h.showTime { - builder.WriteString(e.Timestamp.Format(h.timeFormat)) - builder.WriteString(lx.Newline) + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Newline) } - // Write colored BEGIN separator - builder.WriteString(h.palette.Title) - builder.WriteString("---- BEGIN DUMP ----") - builder.WriteString(h.palette.Reset) - builder.WriteString("\n") + buf.WriteString(h.palette.Title) + buf.WriteString("---- BEGIN DUMP ----") + buf.WriteString(h.palette.Reset) + buf.WriteString("\n") - // Process each line of the dump lines := strings.Split(e.Message, "\n") length := len(lines) for i, line := range lines { if strings.HasPrefix(line, "pos ") { - // Parse and color position and hex/ASCII parts parts := strings.SplitN(line, "hex:", 2) if len(parts) == 2 { - builder.WriteString(h.palette.Pos) - builder.WriteString(parts[0]) - builder.WriteString(h.palette.Reset) + buf.WriteString(h.palette.Pos) + buf.WriteString(parts[0]) + buf.WriteString(h.palette.Reset) hexAscii := strings.SplitN(parts[1], "'", 2) - builder.WriteString(h.palette.Hex) - builder.WriteString("hex:") - builder.WriteString(hexAscii[0]) - builder.WriteString(h.palette.Reset) + buf.WriteString(h.palette.Hex) + buf.WriteString("hex:") + buf.WriteString(hexAscii[0]) + buf.WriteString(h.palette.Reset) if len(hexAscii) > 1 { - builder.WriteString(h.palette.Ascii) - builder.WriteString("'") - builder.WriteString(hexAscii[1]) - builder.WriteString(h.palette.Reset) + buf.WriteString(h.palette.Ascii) + buf.WriteString("'") + buf.WriteString(hexAscii[1]) + buf.WriteString(h.palette.Reset) } } } else if strings.HasPrefix(line, "Dumping value of type:") { - // Color type dump lines - builder.WriteString(h.palette.Header) - builder.WriteString(line) - builder.WriteString(h.palette.Reset) + buf.WriteString(h.palette.Header) + buf.WriteString(line) + buf.WriteString(h.palette.Reset) } else { - // Write non-dump lines as-is - builder.WriteString(line) + buf.WriteString(line) } - // Don't add newline for the last line if i < length-1 { - builder.WriteString("\n") + buf.WriteString("\n") } } - // Write colored END separator - builder.WriteString(h.palette.Title) - builder.WriteString("---- END DUMP ----") - builder.WriteString(h.palette.Reset) - builder.WriteString("\n") + buf.WriteString(h.palette.Title) + buf.WriteString("---- END DUMP ----") + buf.WriteString(h.palette.Reset) + buf.WriteString("\n\n") - // Write formatted output to writer - _, err := h.w.Write([]byte(builder.String())) + _, err := h.writer.Write(buf.Bytes()) return err } // detectPalette selects a color palette based on terminal environment variables. -// It checks TERM_BACKGROUND, COLORFGBG, and AppleInterfaceStyle to determine -// whether a light or dark palette is appropriate, defaulting to darkPalette. -// Example (internal usage): -// -// palette := h.detectPalette() // Returns darkPalette or lightPalette func (h *ColorizedHandler) detectPalette() Palette { - // Check TERM_BACKGROUND (e.g., iTerm2) - if bg, ok := os.LookupEnv("TERM_BACKGROUND"); ok { - if bg == "light" { - return lightPalette // Use light palette for light background - } - return darkPalette // Use dark palette otherwise + // If colors are explicitly disabled, return noColorPalette + if h.noColor { + return noColorPalette } - // Check COLORFGBG (traditional xterm) - if fgBg, ok := os.LookupEnv("COLORFGBG"); ok { + // Check NO_COLOR environment variable (standard: https://no-color.org/) + if os.Getenv("NO_COLOR") != "" { + return noColorPalette + } + + term := os.Getenv("TERM") + if term == "dumb" || term == "" { + if runtime.GOOS == "windows" && !h.isWindowsTerminalAnsiSupported() { + return noColorPalette + } + } + + // First, try to detect background color + isDarkBackground := true // Default to dark + + // Check for common dark/light environment variables + if style, ok := os.LookupEnv("AppleInterfaceStyle"); ok && strings.EqualFold(style, "dark") { + isDarkBackground = true + } else if style, ok := os.LookupEnv("APPEARANCE"); ok && strings.EqualFold(style, "light") { + isDarkBackground = false + } else if bg := os.Getenv("TERM_BACKGROUND"); bg != "" { + isDarkBackground = strings.ToLower(bg) != "light" + } else if fgBg := os.Getenv("COLORFGBG"); fgBg != "" { + // COLORFGBG format: "foreground;background" or "foreground;background;unused" parts := strings.Split(fgBg, ";") if len(parts) >= 2 { - bg := parts[len(parts)-1] // Last part (some terminals add more fields) - if bg == "7" || bg == "15" || bg == "0;15" { // Handle variations - return lightPalette // Use light palette for light background + bg := parts[len(parts)-1] + bgInt, err := strconv.Atoi(bg) + if err == nil { + // According to XTerm documentation: + // 0-7: dark colors, 15: white, 8-14: bright colors + // Typically, 0=black (dark), 7=light gray (light), 15=white (light) + isDarkBackground = (bgInt >= 0 && bgInt <= 6) || (bgInt >= 8 && bgInt <= 14) } } } - // Check macOS dark mode - if style, ok := os.LookupEnv("AppleInterfaceStyle"); ok && strings.EqualFold(style, "dark") { - return darkPalette // Use dark palette for macOS dark mode + if isDarkBackground { + return h.applyIntensity(darkPalette) + } + return h.applyIntensity(lightPalette) +} + +// applyIntensity applies the intensity setting to a base palette +func (h *ColorizedHandler) applyIntensity(basePalette Palette) Palette { + switch h.intensity { + case IntensityNormal: + return basePalette + case IntensityBright: + return brightPalette + case IntensityPastel: + return pastelPalette + case IntensityVibrant: + return vibrantPalette + default: + return basePalette + } +} + +// isWindowsTerminalAnsiSupported checks if the Windows terminal supports ANSI colors +func (h *ColorizedHandler) isWindowsTerminalAnsiSupported() bool { + if runtime.GOOS != "windows" { + return true } - // Default: dark (conservative choice for terminals) - return darkPalette + if os.Getenv("WT_SESSION") != "" { + return true + } + + if os.Getenv("ConEmuANSI") == "ON" { + return true + } + + if os.Getenv("ANSICON") != "" { + return true + } + + return false } diff --git a/vendor/github.com/olekukonko/ll/lh/dedup.go b/vendor/github.com/olekukonko/ll/lh/dedup.go new file mode 100644 index 0000000000..41808560ea --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lh/dedup.go @@ -0,0 +1,163 @@ +package lh + +import ( + "sync" + "time" + + "github.com/olekukonko/ll/lx" +) + +// Dedup is a log handler that suppresses duplicate entries within a TTL window. +// It wraps another handler (H) and filters out repeated log entries that match +// within the deduplication period. +type Dedup[H lx.Handler] struct { + next H + + ttl time.Duration + cleanupEvery time.Duration + keyFn func(*lx.Entry) uint64 + maxKeys int + + // shards reduce lock contention by partitioning the key space + shards [32]dedupShard + + done chan struct{} + wg sync.WaitGroup + once sync.Once +} + +type dedupShard struct { + mu sync.Mutex + seen map[uint64]int64 +} + +// DedupOpt configures a Dedup handler. +type DedupOpt[H lx.Handler] func(*Dedup[H]) + +// WithDedupKeyFunc customizes how deduplication keys are generated. +func WithDedupKeyFunc[H lx.Handler](fn func(*lx.Entry) uint64) DedupOpt[H] { + return func(d *Dedup[H]) { d.keyFn = fn } +} + +// WithDedupCleanupInterval sets how often expired deduplication keys are purged. +func WithDedupCleanupInterval[H lx.Handler](every time.Duration) DedupOpt[H] { + return func(d *Dedup[H]) { + if every > 0 { + d.cleanupEvery = every + } + } +} + +// WithDedupMaxKeys sets a soft limit on tracked deduplication keys. +func WithDedupMaxKeys[H lx.Handler](max int) DedupOpt[H] { + return func(d *Dedup[H]) { + if max > 0 { + d.maxKeys = max + } + } +} + +// NewDedup creates a deduplicating handler wrapper. +func NewDedup[H lx.Handler](next H, ttl time.Duration, opts ...DedupOpt[H]) *Dedup[H] { + if ttl <= 0 { + ttl = 2 * time.Second + } + + d := &Dedup[H]{ + next: next, + ttl: ttl, + cleanupEvery: time.Minute, + keyFn: defaultDedupKey, + done: make(chan struct{}), + } + + // Initialize shards + for i := 0; i < len(d.shards); i++ { + d.shards[i].seen = make(map[uint64]int64, 64) + } + + for _, opt := range opts { + opt(d) + } + + d.wg.Add(1) + go d.cleanupLoop() + + return d +} + +// Handle processes a log entry, suppressing duplicates within the TTL window. +func (d *Dedup[H]) Handle(e *lx.Entry) error { + now := time.Now().UnixNano() + key := d.keyFn(e) + + // Select shard based on key hash + shardIdx := key % uint64(len(d.shards)) + shard := &d.shards[shardIdx] + + shard.mu.Lock() + exp, ok := shard.seen[key] + if ok && now < exp { + shard.mu.Unlock() + return nil + } + + // Basic guard against unbounded growth per shard + // Using strict limits per shard avoids global atomic counters + limitPerShard := d.maxKeys / len(d.shards) + if d.maxKeys > 0 && len(shard.seen) >= limitPerShard { + // Opportunistic cleanup of current shard + d.cleanupShard(shard, now) + } + + shard.seen[key] = now + d.ttl.Nanoseconds() + shard.mu.Unlock() + + return d.next.Handle(e) +} + +// Close stops the cleanup goroutine and closes the underlying handler. +func (d *Dedup[H]) Close() error { + var err error + d.once.Do(func() { + close(d.done) + d.wg.Wait() + + if c, ok := any(d.next).(interface{ Close() error }); ok { + err = c.Close() + } + }) + return err +} + +// cleanupLoop runs periodically to purge expired deduplication keys. +func (d *Dedup[H]) cleanupLoop() { + defer d.wg.Done() + + t := time.NewTicker(d.cleanupEvery) + defer t.Stop() + + for { + select { + case <-t.C: + now := time.Now().UnixNano() + // Cleanup all shards sequentially to avoid massive CPU spike + for i := 0; i < len(d.shards); i++ { + d.shards[i].mu.Lock() + d.cleanupShard(&d.shards[i], now) + d.shards[i].mu.Unlock() + } + case <-d.done: + return + } + } +} + +// cleanupShard removes expired keys from a specific shard. +func (d *Dedup[H]) cleanupShard(shard *dedupShard, now int64) { + for k, exp := range shard.seen { + if now > exp { + delete(shard.seen, k) + } + } +} diff --git a/vendor/github.com/olekukonko/ll/lh/json.go b/vendor/github.com/olekukonko/ll/lh/json.go index c40576d674..6bed34615b 100644 --- a/vendor/github.com/olekukonko/ll/lh/json.go +++ b/vendor/github.com/olekukonko/ll/lh/json.go @@ -1,26 +1,34 @@ package lh import ( + "bytes" "encoding/json" "fmt" - "github.com/olekukonko/ll/lx" "io" "os" "strings" "sync" "time" + + "github.com/olekukonko/ll/lx" ) +var jsonBufPool = sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, +} + // JSONHandler is a handler that outputs log entries as JSON objects. // It formats log entries with timestamp, level, message, namespace, fields, and optional // stack traces or dump segments, writing the result to the provided writer. // Thread-safe with a mutex to protect concurrent writes. type JSONHandler struct { - writer io.Writer // Destination for JSON output - timeFmt string // Format for timestamp (default: RFC3339Nano) - pretty bool // Enable pretty printing with indentation if true - fieldMap map[string]string // Optional mapping for field names (not used in provided code) - mu sync.Mutex // Protects concurrent access to writer + writer io.Writer // Destination for JSON output + timeFmt string // Format for timestamp (default: RFC3339Nano) + pretty bool // Enable pretty printing with indentation if true + //fieldMap map[string]string // Optional mapping for field names (not used in provided code) + mu sync.Mutex // Protects concurrent access to writer } // JsonOutput represents the JSON structure for a log entry. @@ -84,6 +92,13 @@ func (h *JSONHandler) Handle(e *lx.Entry) error { return h.handleRegular(e) } +// Output sets the Writer destination for JSONHandler's output, ensuring thread safety with a mutex lock. +func (h *JSONHandler) Output(w io.Writer) { + h.mu.Lock() + defer h.mu.Unlock() + h.writer = w +} + // handleRegular handles standard log entries (non-dump). // It converts the entry to a JsonOutput struct and encodes it as JSON, // applying pretty printing if enabled. Logs encoding errors to stderr for debugging. @@ -92,6 +107,12 @@ func (h *JSONHandler) Handle(e *lx.Entry) error { // // h.handleRegular(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes JSON object func (h *JSONHandler) handleRegular(e *lx.Entry) error { + // Convert ordered fields to map for JSON output + fieldsMap := make(map[string]interface{}, len(e.Fields)) + for _, pair := range e.Fields { + fieldsMap[pair.Key] = pair.Value + } + // Create JSON output structure entry := JsonOutput{ Time: e.Timestamp.Format(h.timeFmt), // Format timestamp @@ -100,23 +121,32 @@ func (h *JSONHandler) handleRegular(e *lx.Entry) error { Msg: e.Message, // Set message Namespace: e.Namespace, // Set namespace Dump: nil, // No dump for regular entries - Fields: e.Fields, // Copy fields + Fields: fieldsMap, // Copy fields as map Stack: e.Stack, // Include stack trace if present } - // Create JSON encoder - enc := json.NewEncoder(h.writer) + + // Acquire buffer from pool to avoid allocation and reduce syscalls + buf := jsonBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer jsonBufPool.Put(buf) + + // Create JSON encoder writing to buffer + enc := json.NewEncoder(buf) if h.pretty { // Enable indentation for pretty printing enc.SetIndent("", " ") } - // Log encoding attempt for debugging - fmt.Fprintf(os.Stderr, "Encoding JSON entry: %v\n", e.Message) - // Encode and write JSON + + // Encode JSON to buffer err := enc.Encode(entry) if err != nil { // Log encoding error for debugging fmt.Fprintf(os.Stderr, "JSON encode error: %v\n", err) + return err } + + // Write buffer to underlying writer in one go + _, err = h.writer.Write(buf.Bytes()) return err } @@ -156,15 +186,40 @@ func (h *JSONHandler) handleDump(e *lx.Entry) error { }) } - // Encode JSON output with dump segments - return json.NewEncoder(h.writer).Encode(JsonOutput{ + // Convert ordered fields to map for JSON output + fieldsMap := make(map[string]interface{}, len(e.Fields)) + for _, pair := range e.Fields { + fieldsMap[pair.Key] = pair.Value + } + + // Acquire buffer from pool + buf := jsonBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer jsonBufPool.Put(buf) + + // Encode JSON output with dump segments to buffer + enc := json.NewEncoder(buf) + if h.pretty { + enc.SetIndent("", " ") + } + + err := enc.Encode(JsonOutput{ Time: e.Timestamp.Format(h.timeFmt), // Format timestamp Level: e.Level.String(), // Convert level to string Class: e.Class.String(), // Convert class to string Msg: "dumping segments", // Fixed message for dumps Namespace: e.Namespace, // Set namespace Dump: segments, // Include parsed segments - Fields: e.Fields, // Copy fields + Fields: fieldsMap, // Copy fields as map Stack: e.Stack, // Include stack trace if present }) + + if err != nil { + fmt.Fprintf(os.Stderr, "JSON dump encode error: %v\n", err) + return err + } + + // Write buffer to underlying writer + _, err = h.writer.Write(buf.Bytes()) + return err } diff --git a/vendor/github.com/olekukonko/ll/lh/lh.go b/vendor/github.com/olekukonko/ll/lh/lh.go new file mode 100644 index 0000000000..5166c3b51e --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lh/lh.go @@ -0,0 +1,67 @@ +package lh + +import ( + "bytes" + "fmt" + "sort" + "strings" + "sync" + + "github.com/cespare/xxhash/v2" + "github.com/olekukonko/ll/lx" +) + +// rightPad pads a string with spaces on the right to reach the specified length. +// Returns the original string if it's already at or exceeds the target length. +// Uses strings.Builder for efficient memory allocation. +func rightPad(str string, length int) string { + if len(str) >= length { + return str + } + var sb strings.Builder + sb.Grow(length) + sb.WriteString(str) + sb.WriteString(strings.Repeat(" ", length-len(str))) + return sb.String() +} + +var dedupBufPool = sync.Pool{ + New: func() any { return new(bytes.Buffer) }, +} + +// defaultDedupKey generates a deduplication key from log level and message. +// Uses FNV-1a hash for speed and good distribution. Override with WithDedupKeyFunc +// to include additional fields like namespace, caller, or structured fields. +func defaultDedupKey(e *lx.Entry) uint64 { + h := xxhash.New() + + _, _ = h.Write([]byte(e.Level.String())) + _, _ = h.Write([]byte{0}) + _, _ = h.Write([]byte(e.Message)) + _, _ = h.Write([]byte{0}) + _, _ = h.Write([]byte(e.Namespace)) + _, _ = h.Write([]byte{0}) + + if len(e.Fields) > 0 { + m := e.Fields.Map() + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + + buf := dedupBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer dedupBufPool.Put(buf) + + for _, k := range keys { + fmt.Fprint(buf, k) + buf.WriteByte('=') + fmt.Fprint(buf, m[k]) + buf.WriteByte(0) + } + _, _ = h.Write(buf.Bytes()) + } + + return h.Sum64() +} diff --git a/vendor/github.com/olekukonko/ll/lh/memory.go b/vendor/github.com/olekukonko/ll/lh/memory.go index e3bc939873..42fb8928c9 100644 --- a/vendor/github.com/olekukonko/ll/lh/memory.go +++ b/vendor/github.com/olekukonko/ll/lh/memory.go @@ -2,9 +2,10 @@ package lh import ( "fmt" - "github.com/olekukonko/ll/lx" "io" "sync" + + "github.com/olekukonko/ll/lx" ) // MemoryHandler is an lx.Handler that stores log entries in memory. @@ -106,7 +107,7 @@ func (h *MemoryHandler) Dump(w io.Writer) error { // Process each entry through the TextHandler for _, entry := range h.entries { if err := tempHandler.Handle(entry); err != nil { - return fmt.Errorf("failed to dump entry: %w", err) // Wrap and return write errors + return fmt.Errorf("failed to dump entry: %writer", err) // Wrap and return write errors } } return nil diff --git a/vendor/github.com/olekukonko/ll/lh/multi.go b/vendor/github.com/olekukonko/ll/lh/multi.go index 54ca9768da..e5eba6cf2e 100644 --- a/vendor/github.com/olekukonko/ll/lh/multi.go +++ b/vendor/github.com/olekukonko/ll/lh/multi.go @@ -32,15 +32,31 @@ func NewMultiHandler(h ...lx.Handler) *MultiHandler { } // Len returns the number of handlers in the MultiHandler. +// Useful for monitoring or debugging handler composition. +// +// Example: +// +// multi := &MultiHandler{} +// multi.Append(h1, h2, h3) +// count := multi.Len() // Returns 3 func (h *MultiHandler) Len() int { return len(h.Handlers) } -// Append adds one or more lx.Handler instances to the MultiHandler's list of handlers. +// Append adds one or more handlers to the MultiHandler. +// Handlers will receive log entries in the order they were appended. +// This method modifies the MultiHandler in place. +// +// Example: +// +// multi := &MultiHandler{} +// multi.Append( +// lx.NewJSONHandler(os.Stdout), +// lx.NewTextHandler(logFile), +// ) +// // Now multi broadcasts to both stdout and file func (h *MultiHandler) Append(handlers ...lx.Handler) { - for _, e := range handlers { - h.Handlers = append(h.Handlers, e) - } + h.Handlers = append(h.Handlers, handlers...) } // Handle implements the Handler interface, calling Handle on each handler in sequence. @@ -56,7 +72,7 @@ func (h *MultiHandler) Handle(e *lx.Entry) error { if err := handler.Handle(e); err != nil { // fmt.Fprintf(os.Stderr, "MultiHandler error for handler %d: %v\n", i, err) // Wrap error with handler index for context - errs = append(errs, fmt.Errorf("handler %d: %w", i, err)) + errs = append(errs, fmt.Errorf("handler %d: %writer", i, err)) } } // Combine errors into a single error, or return nil if no errors diff --git a/vendor/github.com/olekukonko/ll/lh/pipe.go b/vendor/github.com/olekukonko/ll/lh/pipe.go new file mode 100644 index 0000000000..4594c6b7d8 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lh/pipe.go @@ -0,0 +1,76 @@ +package lh + +import ( + "fmt" + "os" + "time" + + "github.com/olekukonko/ll/lx" +) + +// Pipe chains multiple handler wrappers together, applying them from left to right. +// The wrappers are composed such that the first wrapper in the list becomes +// the innermost layer, and the last wrapper becomes the outermost layer. +// +// Usage pattern: Pipe(baseHandler, wrapper1, wrapper2, wrapper3) +// Result: wrapper3(wrapper2(wrapper1(baseHandler))) +// +// This enables clean, declarative construction of handler middleware chains. +// +// Example - building a processing pipeline: +// +// base := lx.NewJSONHandler(os.Stdout) +// handler := lh.Pipe(base, +// lh.NewDedup(2*time.Second), // 1. Deduplicate first +// lh.NewRateLimit(10, time.Second), // 2. Then rate limit +// ) +// logger := lx.NewLogger(handler) +// +// In this example, logs flow: Dedup → RateLimit → AddTimestamp → JSONHandler +func Pipe(h lx.Handler, wraps ...lx.Wrap) lx.Handler { + for _, w := range wraps { + if w != nil { + h = w(h) + } + } + return h +} + +// PipeDedup returns a wrapper that applies deduplication to the handler. +func PipeDedup(ttl time.Duration, opts ...DedupOpt[lx.Handler]) lx.Wrap { + return func(next lx.Handler) lx.Handler { + return NewDedup(next, ttl, opts...) + } +} + +// PipeBuffer returns a wrapper that applies buffering to the handler. +func PipeBuffer(opts ...BufferingOpt) lx.Wrap { + return func(next lx.Handler) lx.Handler { + return NewBuffered(next, opts...) + } +} + +// PipeRotate returns a wrapper that applies log rotation. +// Ideally, the 'next' handler should be one that writes to a file (like TextHandler or JSONHandler). +// +// If the underlying handler does not implement lx.HandlerOutputter (cannot change output destination), +// or if rotation initialization fails, this will log a warning to stderr and return the +// original handler unmodified to prevent application crashes. +func PipeRotate(maxSizeBytes int64, src RotateSource) lx.Wrap { + return func(next lx.Handler) lx.Handler { + // Attempt to cast to HandlerOutputter (Handler + Outputter interface) + h, ok := next.(lx.HandlerOutputter) + if !ok { + fmt.Fprintf(os.Stderr, "ll/lh: PipeRotate skipped - handler does not implement SetOutput(io.Writer)\n") + return next + } + + // Initialize the rotating handler + r, err := NewRotating(h, maxSizeBytes, src) + if err != nil { + fmt.Fprintf(os.Stderr, "ll/lh: PipeRotate initialization failed: %v\n", err) + return next + } + return r + } +} diff --git a/vendor/github.com/olekukonko/ll/lh/rotate.go b/vendor/github.com/olekukonko/ll/lh/rotate.go new file mode 100644 index 0000000000..68cff5cee4 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lh/rotate.go @@ -0,0 +1,176 @@ +package lh + +import ( + "io" + "sync" + + "github.com/olekukonko/ll/lx" +) + +// RotateSource defines the callbacks needed to implement log rotation. +// It abstracts the destination lifecycle: opening, sizing, and rotating. +// +// Example for file rotation: +// +// src := lh.RotateSource{ +// Open: func() (io.WriteCloser, error) { +// return os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) +// }, +// Size: func() (int64, error) { +// if fi, err := os.Stat("app.log"); err == nil { +// return fi.Size(), nil +// } +// return 0, nil // File doesn't exist yet +// }, +// Rotate: func() error { +// // Rename current log before creating new one +// return os.Rename("app.log", "app.log."+time.Now().Format("20060102-150405")) +// }, +// } +type RotateSource struct { + // Open returns a fresh destination for log output. + // Called on initialization and after rotation. + Open func() (io.WriteCloser, error) + + // Size returns the current size in bytes of the active destination. + // Return an error if size cannot be determined (rotation will be skipped). + Size func() (int64, error) + + // Rotate performs cleanup/rotation actions before opening a new destination. + // For files: rename or move the current log. Optional for other destinations. + Rotate func() error +} + +// Rotating wraps a handler to rotate its output when maxSize is exceeded. +// The wrapped handler must implement both Handler and Outputter interfaces. +// Rotation is triggered on each Handle call if the current size >= maxSize. +// +// Example: +// +// handler := lx.NewJSONHandler(os.Stdout) +// src := lh.RotateSource{...} // see RotateSource example +// rotator, err := lh.NewRotating(handler, 10*1024*1024, src) // 10 MB +// logger := lx.NewLogger(rotator) +// logger.Info("This log may trigger rotation when file reaches 10MB") +type Rotating[H interface { + lx.Handler + lx.Outputter +}] struct { + mu sync.Mutex + maxSize int64 + src RotateSource + + out io.WriteCloser + handler H +} + +// NewRotating creates a rotating wrapper around handler. +// Handler's output will be replaced with destinations from src.Open. +// If maxSizeBytes <= 0, rotation is disabled. +// src.Rotate may be nil if no pre-open actions are needed. +// +// Example: +// +// // Create a JSON handler that rotates at 5MB +// handler := lx.NewJSONHandler(os.Stdout) +// rotator, err := lh.NewRotating(handler, 5*1024*1024, src) +// if err != nil { +// log.Fatal(err) +// } +// // Use rotator as your logger's handler +// logger := lx.NewLogger(rotator) +func NewRotating[H interface { + lx.Handler + lx.Outputter +}](handler H, maxSizeBytes int64, src RotateSource) (*Rotating[H], error) { + r := &Rotating[H]{ + maxSize: maxSizeBytes, + src: src, + handler: handler, + } + if err := r.reopenLocked(); err != nil { + return nil, err + } + return r, nil +} + +// Handle processes a log entry, rotating output if necessary. +// Thread-safe: can be called concurrently. +// +// Example: +// +// rotator.Handle(&lx.Entry{ +// Level: lx.InfoLevel, +// Message: "Processing request", +// Namespace: "api", +// }) +func (r *Rotating[H]) Handle(e *lx.Entry) error { + r.mu.Lock() + defer r.mu.Unlock() + + if err := r.rotateIfNeededLocked(); err != nil { + return err + } + return r.handler.Handle(e) +} + +// Close releases resources (closes the current output). +// Safe to call multiple times. +// +// Example: +// +// defer rotator.Close() +func (r *Rotating[H]) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + + if r.out != nil { + return r.out.Close() + } + return nil +} + +// rotateIfNeededLocked checks current size and rotates if maxSize exceeded. +// Called with mu already held. +func (r *Rotating[H]) rotateIfNeededLocked() error { + if r.maxSize <= 0 || r.src.Size == nil || r.src.Open == nil { + return nil + } + + size, err := r.src.Size() + if err != nil { + // Size unknown - skip rotation + return nil + } + if size < r.maxSize { + return nil + } + + // Close current output + if r.out != nil { + _ = r.out.Close() + r.out = nil + } + + // Run rotation hook (rename/move/commit) + if r.src.Rotate != nil { + if err := r.src.Rotate(); err != nil { + return err + } + } + + // Open fresh output + return r.reopenLocked() +} + +// reopenLocked opens a new destination and sets it on the handler. +// Called with mu already held. +func (r *Rotating[H]) reopenLocked() error { + out, err := r.src.Open() + if err != nil { + return err + } + r.out = out + r.handler.Output(out) + return nil +} diff --git a/vendor/github.com/olekukonko/ll/lh/slog.go b/vendor/github.com/olekukonko/ll/lh/slog.go index 0b738d9738..e457f2ecba 100644 --- a/vendor/github.com/olekukonko/ll/lh/slog.go +++ b/vendor/github.com/olekukonko/ll/lh/slog.go @@ -28,6 +28,15 @@ func NewSlogHandler(h slog.Handler) *SlogHandler { return &SlogHandler{slogHandler: h} } +// Handle converts an lx.Entry to slog.Record and delegates to the slog.Handler. +// It maps the entry's fields, level, namespace, class, and stack trace to slog attributes, +// passing the resulting record to the underlying slog.Handler. +// Returns an error if the slog.Handler fails to process the record. +// Thread-safe if the underlying slog.Handler is thread-safe. +// Example: +// +// handler.Handle(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Processes as slog record +// // Handle converts an lx.Entry to slog.Record and delegates to the slog.Handler. // It maps the entry's fields, level, namespace, class, and stack trace to slog attributes, // passing the resulting record to the underlying slog.Handler. @@ -59,9 +68,9 @@ func (h *SlogHandler) Handle(e *lx.Entry) error { record.AddAttrs(slog.String("stack", string(e.Stack))) // Add stack trace as string } - // Add custom fields - for k, v := range e.Fields { - record.AddAttrs(slog.Any(k, v)) // Add each field as a key-value attribute + // Add custom fields in order (preserving insertion order) + for _, pair := range e.Fields { + record.AddAttrs(slog.Any(pair.Key, pair.Value)) // Add each field as a key-value attribute } // Handle the record with the underlying slog.Handler diff --git a/vendor/github.com/olekukonko/ll/lh/text.go b/vendor/github.com/olekukonko/ll/lh/text.go index 0b88cf4b87..2bd59ff70e 100644 --- a/vendor/github.com/olekukonko/ll/lh/text.go +++ b/vendor/github.com/olekukonko/ll/lh/text.go @@ -1,9 +1,9 @@ package lh import ( + "bytes" "fmt" "io" - "sort" "strings" "sync" "time" @@ -11,12 +11,40 @@ import ( "github.com/olekukonko/ll/lx" ) +type TextOption func(*TextHandler) + +var textBufPool = sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, +} + +// WithTextTimeFormat enables timestamp display and optionally sets a custom time format. +// It configures the TextHandler to include temporal information in each log entry, +// allowing for precise tracking of when log events occur. +// If the format string is empty, it defaults to time.RFC3339. +func WithTextTimeFormat(format string) TextOption { + return func(t *TextHandler) { + t.Timestamped(true, format) + } +} + +// WithTextShowTime enables or disables timestamp display in log entries. +// This option provides direct control over the visibility of the time prefix +// without altering the underlying time format configured in the handler. +// Setting show to true will prepend timestamps to all subsequent regular log outputs. +func WithTextShowTime(show bool) TextOption { + return func(t *TextHandler) { + t.showTime = show + } +} + // TextHandler is a handler that outputs log entries as plain text. // It formats log entries with namespace, level, message, fields, and optional stack traces, // writing the result to the provided writer. // Thread-safe if the underlying writer is thread-safe. type TextHandler struct { - w io.Writer // Destination for formatted log output + writer io.Writer // Destination for formatted log output showTime bool // Whether to display timestamps timeFormat string // Format for timestamps (defaults to time.RFC3339) mu sync.Mutex @@ -29,12 +57,18 @@ type TextHandler struct { // handler := NewTextHandler(os.Stdout) // logger := ll.New("app").Enable().Handler(handler) // logger.Info("Test") // Output: [app] INFO: Test -func NewTextHandler(w io.Writer) *TextHandler { - return &TextHandler{ - w: w, +func NewTextHandler(w io.Writer, opts ...TextOption) *TextHandler { + t := &TextHandler{ + writer: w, showTime: false, timeFormat: time.RFC3339, } + + for _, opt := range opts { + opt(t) + } + + return t } // Timestamped enables or disables timestamp display and optionally sets a custom time format. @@ -50,6 +84,14 @@ func (h *TextHandler) Timestamped(enable bool, format ...string) { } } +// Output sets a new writer for the TextHandler. +// Thread-safe - safe for concurrent use. +func (h *TextHandler) Output(w io.Writer) { + h.mu.Lock() + defer h.mu.Unlock() + h.writer = w +} + // Handle processes a log entry and writes it as plain text. // It delegates to specialized methods based on the entry's class (Dump, Raw, or regular). // Returns an error if writing to the underlying writer fails. @@ -61,18 +103,15 @@ func (h *TextHandler) Handle(e *lx.Entry) error { h.mu.Lock() defer h.mu.Unlock() - // Special handling for dump output if e.Class == lx.ClassDump { return h.handleDumpOutput(e) } - // Raw entries are written directly without formatting if e.Class == lx.ClassRaw { - _, err := h.w.Write([]byte(e.Message)) + _, err := h.writer.Write([]byte(e.Message)) return err } - // Handle standard log entries return h.handleRegularOutput(e) } @@ -84,81 +123,68 @@ func (h *TextHandler) Handle(e *lx.Entry) error { // // h.handleRegularOutput(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes "INFO: test" func (h *TextHandler) handleRegularOutput(e *lx.Entry) error { - var builder strings.Builder // Buffer for building formatted output + buf := textBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer textBufPool.Put(buf) - // Add timestamp if enabled if h.showTime { - builder.WriteString(e.Timestamp.Format(h.timeFormat)) - builder.WriteString(lx.Space) + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Space) } - // Format namespace based on style switch e.Style { case lx.NestedPath: if e.Namespace != "" { - // Split namespace into parts and format as [parent]→[child] parts := strings.Split(e.Namespace, lx.Slash) for i, part := range parts { - builder.WriteString(lx.LeftBracket) - builder.WriteString(part) - builder.WriteString(lx.RightBracket) + buf.WriteString(lx.LeftBracket) + buf.WriteString(part) + buf.WriteString(lx.RightBracket) if i < len(parts)-1 { - builder.WriteString(lx.Arrow) + buf.WriteString(lx.Arrow) } } - builder.WriteString(lx.Colon) - builder.WriteString(lx.Space) + buf.WriteString(lx.Colon) + buf.WriteString(lx.Space) } default: // FlatPath if e.Namespace != "" { - // Format namespace as [parent/child] - builder.WriteString(lx.LeftBracket) - builder.WriteString(e.Namespace) - builder.WriteString(lx.RightBracket) - builder.WriteString(lx.Space) + buf.WriteString(lx.LeftBracket) + buf.WriteString(e.Namespace) + buf.WriteString(lx.RightBracket) + buf.WriteString(lx.Space) } } - // Add level and message - builder.WriteString(e.Level.String()) - builder.WriteString(lx.Colon) - builder.WriteString(lx.Space) - builder.WriteString(e.Message) + buf.WriteString(e.Level.Name(e.Class)) + // buf.WriteString(lx.Space) + buf.WriteString(lx.Colon) + buf.WriteString(lx.Space) + buf.WriteString(e.Message) - // Add fields in sorted order if len(e.Fields) > 0 { - var keys []string - for k := range e.Fields { - keys = append(keys, k) - } - // Sort keys for consistent output - sort.Strings(keys) - builder.WriteString(lx.Space) - builder.WriteString(lx.LeftBracket) - for i, k := range keys { + buf.WriteString(lx.Space) + buf.WriteString(lx.LeftBracket) + for i, pair := range e.Fields { if i > 0 { - builder.WriteString(lx.Space) + buf.WriteString(lx.Space) } - // Format field as key=value - builder.WriteString(k) - builder.WriteString("=") - builder.WriteString(fmt.Sprint(e.Fields[k])) + buf.WriteString(pair.Key) + buf.WriteString("=") + fmt.Fprint(buf, pair.Value) } - builder.WriteString(lx.RightBracket) + buf.WriteString(lx.RightBracket) } - // Add stack trace if present if len(e.Stack) > 0 { - h.formatStack(&builder, e.Stack) + h.formatStack(buf, e.Stack) } - // Append newline for non-None levels if e.Level != lx.LevelNone { - builder.WriteString(lx.Newline) + buf.WriteString(lx.Newline) } - // Write formatted output to writer - _, err := h.w.Write([]byte(builder.String())) + _, err := h.writer.Write(buf.Bytes()) return err } @@ -169,22 +195,20 @@ func (h *TextHandler) handleRegularOutput(e *lx.Entry) error { // // h.handleDumpOutput(&lx.Entry{Class: lx.ClassDump, Message: "pos 00 hex: 61"}) // Writes "---- BEGIN DUMP ----\npos 00 hex: 61\n---- END DUMP ----\n" func (h *TextHandler) handleDumpOutput(e *lx.Entry) error { - // For text handler, we just add a newline before dump output - var builder strings.Builder // Buffer for building formatted output + buf := textBufPool.Get().(*bytes.Buffer) + buf.Reset() + defer textBufPool.Put(buf) - // Add timestamp if enabled if h.showTime { - builder.WriteString(e.Timestamp.Format(h.timeFormat)) - builder.WriteString(lx.Newline) + buf.WriteString(e.Timestamp.Format(h.timeFormat)) + buf.WriteString(lx.Newline) } - // Add separator lines and dump content - builder.WriteString("---- BEGIN DUMP ----\n") - builder.WriteString(e.Message) - builder.WriteString("---- END DUMP ----\n") + buf.WriteString("---- BEGIN DUMP ----\n") + buf.WriteString(e.Message) + buf.WriteString("---- END DUMP ----\n\n") - // Write formatted output to writer - _, err := h.w.Write([]byte(builder.String())) + _, err := h.writer.Write(buf.Bytes()) return err } @@ -194,21 +218,18 @@ func (h *TextHandler) handleDumpOutput(e *lx.Entry) error { // Example (internal usage): // // h.formatStack(&builder, []byte("goroutine 1 [running]:\nmain.main()\n\tmain.go:10")) // Appends formatted stack trace -func (h *TextHandler) formatStack(b *strings.Builder, stack []byte) { +func (h *TextHandler) formatStack(b *bytes.Buffer, stack []byte) { lines := strings.Split(string(stack), "\n") if len(lines) == 0 { return } - // Start stack trace section b.WriteString("\n[stack]\n") - // First line: goroutine b.WriteString(" ┌─ ") b.WriteString(lines[0]) b.WriteString("\n") - // Iterate through remaining lines for i := 1; i < len(lines); i++ { line := strings.TrimSpace(lines[i]) if line == "" { @@ -216,16 +237,13 @@ func (h *TextHandler) formatStack(b *strings.Builder, stack []byte) { } if strings.Contains(line, ".go") { - // File path lines get extra indent b.WriteString(" ├ ") } else { - // Function names b.WriteString(" │ ") } b.WriteString(line) b.WriteString("\n") } - // End stack trace section b.WriteString(" └\n") } diff --git a/vendor/github.com/olekukonko/ll/ll.go b/vendor/github.com/olekukonko/ll/ll.go index 6505f8d2a9..4dfafdd946 100644 --- a/vendor/github.com/olekukonko/ll/ll.go +++ b/vendor/github.com/olekukonko/ll/ll.go @@ -1,7 +1,6 @@ package ll import ( - "bufio" "encoding/binary" "encoding/json" "fmt" @@ -24,23 +23,25 @@ import ( // log level, namespaces, context fields, output style, handler, middleware, and formatting. // It is thread-safe, using a read-write mutex to protect concurrent access to its fields. type Logger struct { - mu sync.RWMutex // Guards concurrent access to fields - enabled bool // Determines if logging is enabled - suspend atomic.Bool // uses suspend path for most actions eg. skipping namespace checks - level lx.LevelType // Minimum log level (e.g., Debug, Info, Warn, Error) - namespaces *lx.Namespace // Manages namespace enable/disable states - currentPath string // Current namespace path (e.g., "parent/child") - context map[string]interface{} // Contextual fields included in all logs - style lx.StyleType // Namespace formatting style (FlatPath or NestedPath) - handler lx.Handler // Output handler for logs (e.g., text, JSON) - middleware []Middleware // Middleware functions to process log entries - prefix string // Prefix prepended to log messages - indent int // Number of double spaces for message indentation - stackBufferSize int // Buffer size for capturing stack traces - separator string // Separator for namespace paths (e.g., "/") - entries atomic.Int64 // Tracks total log entries sent to handler + mu sync.RWMutex // Guards concurrent access to fields + enabled bool // Determines if logging is enabled + suspend atomic.Bool // uses suspend path for most actions eg. skipping namespace checks + level lx.LevelType // Minimum log level (e.g., Debug, Info, Warn, Error) + atomicLevel int32 // Shadow copy of level for lock-free checks + namespaces *lx.Namespace // Manages namespace enable/disable states + currentPath string // Current namespace path (e.g., "parent/child") + context lx.Fields // Contextual fields included in all logs + style lx.StyleType // Namespace formatting style (FlatPath or NestedPath) + handler lx.Handler // Output handler for logs (e.g., text, JSON) + middleware []Middleware // Middleware functions to process log entries + prefix string // Prefix prepended to log messages + indent int // Number of double spaces for message indentation + stackBufferSize int // Buffer size for capturing stack traces + separator string // Separator for namespace paths (e.g., "/") + entries atomic.Int64 // Tracks total log entries sent to handler fatalExits bool fatalStack bool + labels atomic.Pointer[[]string] } // New creates a new Logger with the given namespace and optional configurations. @@ -55,9 +56,10 @@ func New(namespace string, opts ...Option) *Logger { logger := &Logger{ enabled: lx.DefaultEnabled, // Defaults to disabled (false) level: lx.LevelDebug, // Default minimum log level + atomicLevel: int32(lx.LevelDebug), // Initialize atomic level namespaces: defaultStore, // Shared namespace store currentPath: namespace, // Initial namespace path - context: make(map[string]interface{}), // Empty context for fields + context: make(lx.Fields, 0, 10), // Empty context for fields style: lx.FlatPath, // Default namespace style ([parent/child]) handler: lh.NewTextHandler(os.Stdout), // Default text output to stdout middleware: make([]Middleware, 0), // Empty middleware chain @@ -116,28 +118,15 @@ func (l *Logger) AddContext(pairs ...any) *Logger { l.mu.Lock() defer l.mu.Unlock() - // Lazy initialization of context map if l.context == nil { - l.context = make(map[string]interface{}) + l.context = make(lx.Fields, 0, len(pairs)/2) } - // Process key-value pairs for i := 0; i < len(pairs)-1; i += 2 { - key, ok := pairs[i].(string) - if !ok { - l.Warnf("AddContext: non-string key at index %d: %v", i, pairs[i]) - continue + if key, ok := pairs[i].(string); ok { + l.context = append(l.context, lx.Field{Key: key, Value: pairs[i+1]}) } - - value := pairs[i+1] - l.context[key] = value } - - // Optional: warn about uneven number of arguments - if len(pairs)%2 != 0 { - l.Warn("AddContext: uneven number of arguments, last value ignored") - } - return l } @@ -195,18 +184,19 @@ func (l *Logger) Clone() *Logger { defer l.mu.RUnlock() return &Logger{ - enabled: l.enabled, // Copy enablement state - level: l.level, // Copy log level - namespaces: l.namespaces, // Share namespace store - currentPath: l.currentPath, // Copy namespace path - context: make(map[string]interface{}), // Fresh context map - style: l.style, // Copy namespace style - handler: l.handler, // Copy output handler - middleware: l.middleware, // Copy middleware chain - prefix: l.prefix, // Copy message prefix - indent: l.indent, // Copy indentation level - stackBufferSize: l.stackBufferSize, // Copy stack trace buffer size - separator: l.separator, // Default separator ("/") + enabled: l.enabled, // Copy enablement state + level: l.level, // Copy log level + atomicLevel: l.atomicLevel, // Copy atomic level + namespaces: l.namespaces, // Share namespace store + currentPath: l.currentPath, // Copy namespace path + context: make(lx.Fields, 0, 10), // Fresh context map + style: l.style, // Copy namespace style + handler: l.handler, // Copy output handler + middleware: l.middleware, // Copy middleware chain + prefix: l.prefix, // Copy message prefix + indent: l.indent, // Copy indentation level + stackBufferSize: l.stackBufferSize, // Copy stack trace buffer size + separator: l.separator, // Default separator ("/") suspend: l.suspend, } } @@ -227,9 +217,10 @@ func (l *Logger) Context(fields map[string]interface{}) *Logger { newLogger := &Logger{ enabled: l.enabled, level: l.level, + atomicLevel: l.atomicLevel, namespaces: l.namespaces, currentPath: l.currentPath, - context: make(map[string]interface{}), + context: make(lx.Fields, 0, len(l.context)+len(fields)), style: l.style, handler: l.handler, middleware: l.middleware, @@ -238,37 +229,21 @@ func (l *Logger) Context(fields map[string]interface{}) *Logger { stackBufferSize: l.stackBufferSize, separator: l.separator, suspend: l.suspend, + fatalExits: l.fatalExits, + fatalStack: l.fatalStack, } - // Copy parent's context fields - for k, v := range l.context { - newLogger.context[k] = v - } + // Copy parent's context fields (in order) + newLogger.context = append(newLogger.context, l.context...) - // Add new fields + // Add new fields from map for k, v := range fields { - newLogger.context[k] = v + newLogger.context = append(newLogger.context, lx.Field{Key: k, Value: v}) } return newLogger } -// Dbg logs debug information, including the source file, line number, and expression -// value, capturing the calling line of code. It is useful for debugging without temporary -// print statements. -// Example: -// -// x := 42 -// logger.Dbg(x) // Output: [file.go:123] x = 42 -func (l *Logger) Dbg(values ...interface{}) { - // Skip logging if Info level is not enabled - if !l.shouldLog(lx.LevelInfo) { - return - } - - l.dbg(2, values...) -} - // Debug logs a message at Debug level, formatting it and delegating to the internal // log method. It is thread-safe. // Example: @@ -434,7 +409,7 @@ func (l *Logger) output(skip int, values ...interface{}) { "error": err.Error(), }, " ", " ") } - l.log(lx.LevelInfo, lx.ClassText, header+string(b), nil, false) + l.log(lx.LevelInfo, lx.ClassJSON, header+string(b), nil, false) } } @@ -503,10 +478,11 @@ func (l *Logger) Err(errs ...error) { } l.mu.Lock() + defer l.mu.Unlock() - // Initialize context map if nil + // Initialize context slice if nil if l.context == nil { - l.context = make(map[string]interface{}) + l.context = make(lx.Fields, 0, 4) } // Collect non-nil errors and build log message @@ -527,15 +503,14 @@ func (l *Logger) Err(errs ...error) { if count > 0 { if count == 1 { // Store single error directly - l.context["error"] = nonNilErrors[0] + l.context = append(l.context, lx.Field{Key: "error", Value: nonNilErrors[0]}) } else { // Store slice of errors - l.context["error"] = nonNilErrors + l.context = append(l.context, lx.Field{Key: "error", Value: nonNilErrors}) } // Log concatenated error messages l.log(lx.LevelError, lx.ClassText, builder.String(), nil, false) } - l.mu.Unlock() } // Error logs a message at Error level, formatting it and delegating to the internal @@ -616,29 +591,27 @@ func (l *Logger) Fatalf(format string, args ...any) { // logger := New("app").Enable() // logger.Field(map[string]interface{}{"user": "alice"}).Info("Action") // Output: [app] INFO: Action [user=alice] func (l *Logger) Field(fields map[string]interface{}) *FieldBuilder { - fb := &FieldBuilder{logger: l, fields: make(map[string]interface{})} + fb := &FieldBuilder{logger: l, fields: make(lx.Fields, 0, len(fields))} - // check if suspended if l.suspend.Load() { return fb } - // Copy fields from input map to FieldBuilder + // Copy fields from input map to FieldBuilder (preserving map iteration order) for k, v := range fields { - fb.fields[k] = v + fb.fields = append(fb.fields, lx.Field{Key: k, Value: v}) } return fb } -// Fields starts a fluent chain for adding fields using variadic key-value pairs, -// creating a FieldBuilder. Non-string keys or uneven pairs add an error field. It is -// thread-safe via the FieldBuilder’s logger. +// Fields starts a fluent chain for adding fields using variadic key-value pairs. +// It creates a FieldBuilder to attach fields, handling non-string keys or uneven pairs by +// adding an error field. Thread-safe via the FieldBuilder's logger. // Example: // -// logger := New("app").Enable() // logger.Fields("user", "alice").Info("Action") // Output: [app] INFO: Action [user=alice] func (l *Logger) Fields(pairs ...any) *FieldBuilder { - fb := &FieldBuilder{logger: l, fields: make(map[string]interface{})} + fb := &FieldBuilder{logger: l, fields: make(lx.Fields, 0, len(pairs)/2)} if l.suspend.Load() { return fb @@ -647,15 +620,21 @@ func (l *Logger) Fields(pairs ...any) *FieldBuilder { // Process key-value pairs for i := 0; i < len(pairs)-1; i += 2 { if key, ok := pairs[i].(string); ok { - fb.fields[key] = pairs[i+1] + fb.fields = append(fb.fields, lx.Field{Key: key, Value: pairs[i+1]}) } else { // Log error for non-string keys - fb.fields["error"] = fmt.Errorf("non-string key in Fields: %v", pairs[i]) + fb.fields = append(fb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("non-string key in Fields: %v", pairs[i]), + }) } } // Log error for uneven pairs if len(pairs)%2 != 0 { - fb.fields["error"] = fmt.Errorf("uneven key-value pairs in Fields: [%v]", pairs[len(pairs)-1]) + fb.fields = append(fb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("uneven key-value pairs in Fields: [%v]", pairs[len(pairs)-1]), + }) } return fb } @@ -669,7 +648,12 @@ func (l *Logger) Fields(pairs ...any) *FieldBuilder { func (l *Logger) GetContext() map[string]interface{} { l.mu.RLock() defer l.mu.RUnlock() - return l.context + // Convert slice to map for backward compatibility + contextMap := make(map[string]interface{}, len(l.context)) + for _, pair := range l.context { + contextMap[pair.Key] = pair.Value + } + return contextMap } // GetHandler returns the logger's current handler for customization or inspection. @@ -801,6 +785,28 @@ func (l *Logger) Len() int64 { return l.entries.Load() } +// Labels temporarily attaches one or more label names to the logger for the next log entry. +// Labels are typically used for metrics, benchmarking, tracing, or categorizing logs in a structured way. +// +// The labels are stored atomically and intended to be short-lived, applying only to the next +// log operation (or until overwritten by a subsequent call to Labels). Multiple labels can +// be provided as separate string arguments. +// +// Example usage: +// +// logger := New("app").Enable() +// +// // Add labels for a specific operation +// logger.Labels("load_users", "process_orders").Measure(func() { +// // ... perform work ... +// }, func() { +// // ... optional callback ... +// }) +func (l *Logger) Labels(names ...string) *Logger { + l.labels.Store(&names) // store temporarily + return l +} + // Level sets the minimum log level, ignoring messages below it. It is thread-safe using // a write lock and returns the logger for chaining. // Example: @@ -812,6 +818,7 @@ func (l *Logger) Level(level lx.LevelType) *Logger { l.mu.Lock() defer l.mu.Unlock() l.level = level + atomic.StoreInt32(&l.atomicLevel, int32(level)) return l } @@ -879,32 +886,6 @@ func (l *Logger) mark(skip int, names ...string) { l.log(lx.LevelInfo, lx.ClassRaw, out, nil, false) } -// Measure benchmarks function execution, logging the duration at Info level with a -// "duration" field. It is thread-safe via Fields and log methods. -// Example: -// -// logger := New("app").Enable() -// duration := logger.Measure(func() { time.Sleep(time.Millisecond) }) -// // Output: [app] INFO: function executed [duration=~1ms] -func (l *Logger) Measure(fns ...func()) time.Duration { - start := time.Now() - - for _, fn := range fns { - if fn != nil { - fn() - } - } - - duration := time.Since(start) - l.Fields( - "duration_ns", duration.Nanoseconds(), - "duration", duration.String(), - "duration_ms", fmt.Sprintf("%.3fms", float64(duration.Nanoseconds())/1e6), - ).Infof("execution completed") - - return duration -} - // Namespace creates a child logger with a sub-namespace appended to the current path, // inheriting the parent’s configuration but with an independent context. It is thread-safe // using a read lock. @@ -931,9 +912,10 @@ func (l *Logger) Namespace(name string) *Logger { return &Logger{ enabled: l.enabled, level: l.level, + atomicLevel: l.atomicLevel, namespaces: l.namespaces, currentPath: fullPath, - context: make(map[string]interface{}), + context: make(lx.Fields, 0, 10), style: l.style, handler: l.handler, middleware: l.middleware, @@ -1253,6 +1235,17 @@ func (l *Logger) Timestamped(enable bool, format ...string) *Logger { return l } +// Toggle enables or disables the logger based on the provided boolean value and returns the updated logger instance. +func (l *Logger) Toggle(v bool) *Logger { + if v { + l.Resume() + return l.Enable() + } + + l.Suspend() + return l.Disable() +} + // Use adds a middleware function to process log entries before they are handled, returning // a Middleware handle for removal. Middleware returning a non-nil error stops the log. // It is thread-safe using a write lock. @@ -1315,58 +1308,6 @@ func (l *Logger) Warnf(format string, args ...any) { l.Warn(fmt.Sprintf(format, args...)) } -// dbg is an internal helper for Dbg, logging debug information with source file and line -// number, extracting the calling line of code. It is thread-safe via the log method. -// Example (internal usage): -// -// logger.Dbg(x) // Calls dbg(2, x) -func (l *Logger) dbg(skip int, values ...interface{}) { - for _, exp := range values { - // Get caller information (file, line) - _, file, line, ok := runtime.Caller(skip) - if !ok { - l.log(lx.LevelError, lx.ClassText, "Dbg: Unable to parse runtime caller", nil, false) - return - } - - // Open source file - f, err := os.Open(file) - if err != nil { - l.log(lx.LevelError, lx.ClassText, "Dbg: Unable to open expected file", nil, false) - return - } - - // Scan file to find the line - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanLines) - var out string - i := 1 - for scanner.Scan() { - if i == line { - // Extract expression between parentheses - v := scanner.Text()[strings.Index(scanner.Text(), "(")+1 : len(scanner.Text())-strings.Index(reverseString(scanner.Text()), ")")-1] - // Format output with file, line, expression, and value - out = fmt.Sprintf("[%s:%d] %s = %+v", file[len(file)-strings.Index(reverseString(file), "/"):], line, v, exp) - break - } - i++ - } - if err := scanner.Err(); err != nil { - l.log(lx.LevelError, lx.ClassText, err.Error(), nil, false) - return - } - // Log based on value type - switch exp.(type) { - case error: - l.log(lx.LevelError, lx.ClassText, out, nil, false) - default: - l.log(lx.LevelInfo, lx.ClassText, out, nil, false) - } - - f.Close() - } -} - // joinPath joins a base path and a relative path using the logger's separator, handling // empty base or relative paths. It is used internally for namespace path construction. // Example (internal usage): @@ -1394,7 +1335,7 @@ func (l *Logger) joinPath(base, relative string) string { // // logger := New("app").Enable() // logger.Info("Test") // Calls log(lx.LevelInfo, "Test", nil, false) -func (l *Logger) log(level lx.LevelType, class lx.ClassType, msg string, fields map[string]interface{}, withStack bool) { +func (l *Logger) log(level lx.LevelType, class lx.ClassType, msg string, fields lx.Fields, withStack bool) { // Skip logging if level is not enabled if !l.shouldLog(level) { return @@ -1408,9 +1349,6 @@ func (l *Logger) log(level lx.LevelType, class lx.ClassType, msg string, fields buf := make([]byte, l.stackBufferSize) l.mu.RUnlock() n := runtime.Stack(buf, false) - if fields == nil { - fields = make(map[string]interface{}) - } stack = buf[:n] } @@ -1428,30 +1366,33 @@ func (l *Logger) log(level lx.LevelType, class lx.ClassType, msg string, fields builder.WriteString(msg) finalMsg := builder.String() - // Create log entry + // Create combined fields slice - THIS PRESERVES ORDER! + // Optimized slice allocation + var combinedFields lx.Fields + if len(l.context) == 0 { + combinedFields = fields + } else if len(fields) == 0 { + combinedFields = l.context + } else { + combinedFields = make(lx.Fields, 0, len(l.context)+len(fields)) + // Add context fields first (in order) + combinedFields = append(combinedFields, l.context...) + // Add immediate fields + combinedFields = append(combinedFields, fields...) + } + + // Create log entry with ordered fields entry := &lx.Entry{ Timestamp: time.Now(), Level: level, Message: finalMsg, Namespace: l.currentPath, - Fields: fields, + Fields: combinedFields, // Already ordered! Style: l.style, Class: class, Stack: stack, } - // Merge context fields, avoiding overwrites - if len(l.context) > 0 { - if entry.Fields == nil { - entry.Fields = make(map[string]interface{}) - } - for k, v := range l.context { - if _, exists := entry.Fields[k]; !exists { - entry.Fields[k] = v - } - } - } - // Apply middleware, stopping if any returns an error for _, mw := range l.middleware { if err := mw.fn.Handle(entry); err != nil { @@ -1486,8 +1427,8 @@ func (l *Logger) shouldLog(level lx.LevelType) bool { return false } - // Skip if log level is below minimum - if level > l.level { + // Atomic fast path: read level without lock + if level > lx.LevelType(atomic.LoadInt32(&l.atomicLevel)) { return false } diff --git a/vendor/github.com/olekukonko/ll/lx/field.go b/vendor/github.com/olekukonko/ll/lx/field.go new file mode 100644 index 0000000000..f662201c01 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lx/field.go @@ -0,0 +1,140 @@ +package lx + +import ( + "fmt" + "strings" +) + +// Field represents a key-value pair where the key is a string and the value is of any type. +type Field struct { + Key string + Value interface{} +} + +// Fields represents a slice of key-value pairs. +type Fields []Field + +// Map converts the Fields slice to a map[string]interface{}. +// This is useful for backward compatibility or when map operations are needed. +// Example: +// +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} +// m := fields.Map() // Returns map[string]interface{}{"user": "alice", "age": 30} +func (f Fields) Map() map[string]interface{} { + m := make(map[string]interface{}, len(f)) + for _, pair := range f { + m[pair.Key] = pair.Value + } + return m +} + +// Get returns the value for a given key and a boolean indicating if the key was found. +// This provides O(n) lookup, which is fine for small numbers of fields. +// Example: +// +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} +// value, found := fields.Get("user") // Returns "alice", true +func (f Fields) Get(key string) (interface{}, bool) { + for _, pair := range f { + if pair.Key == key { + return pair.Value, true + } + } + return nil, false +} + +// Filter returns a new Fields slice containing only pairs where the predicate returns true. +// Example: +// +// fields := lx.Fields{{"user", "alice"}, {"password", "secret"}, {"age", 30}} +// filtered := fields.Filter(func(key string, value interface{}) bool { +// return key != "password" // Remove sensitive fields +// }) +func (f Fields) Filter(predicate func(key string, value interface{}) bool) Fields { + result := make(Fields, 0, len(f)) + for _, pair := range f { + if predicate(pair.Key, pair.Value) { + result = append(result, pair) + } + } + return result +} + +// Translate returns a new Fields slice with keys translated according to the provided mapping. +// Keys not in the mapping are passed through unchanged. This is useful for adapters like Victoria. +// Example: +// +// fields := lx.Fields{{"user", "alice"}, {"timestamp", time.Now()}} +// translated := fields.Translate(map[string]string{ +// "user": "username", +// "timestamp": "ts", +// }) +// // Returns: {{"username", "alice"}, {"ts", time.Now()}} +func (f Fields) Translate(mapping map[string]string) Fields { + result := make(Fields, len(f)) + for i, pair := range f { + if newKey, ok := mapping[pair.Key]; ok { + result[i] = Field{Key: newKey, Value: pair.Value} + } else { + result[i] = pair + } + } + return result +} + +// Merge merges another Fields slice into this one, with the other slice's fields taking precedence +// for duplicate keys (overwrites existing keys). +// Example: +// +// base := lx.Fields{{"user", "alice"}, {"age", 30}} +// additional := lx.Fields{{"age", 31}, {"city", "NYC"}} +// merged := base.Merge(additional) +// // Returns: {{"user", "alice"}, {"age", 31}, {"city", "NYC"}} +func (f Fields) Merge(other Fields) Fields { + result := make(Fields, 0, len(f)+len(other)) + + // Create a map to track which keys from 'other' we've seen + seen := make(map[string]bool, len(other)) + + // First add all fields from 'f' + result = append(result, f...) + + // Then add fields from 'other', overwriting duplicates + for _, pair := range other { + // Check if this key already exists in result + found := false + for i, existing := range result { + if existing.Key == pair.Key { + result[i] = pair // Overwrite + found = true + break + } + } + if !found { + result = append(result, pair) + } + seen[pair.Key] = true + } + + return result +} + +// String returns a human-readable string representation of the fields. +// Example: +// +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} +// str := fields.String() // Returns: "[user=alice age=30]" +func (f Fields) String() string { + var builder strings.Builder + builder.WriteString(LeftBracket) + for i, pair := range f { + if i > 0 { + builder.WriteString(Space) + } + builder.WriteString(pair.Key) + builder.WriteString("=") + builder.WriteString(fmt.Sprint(pair.Value)) + } + builder.WriteString(RightBracket) + return builder.String() +} diff --git a/vendor/github.com/olekukonko/ll/lx/interface.go b/vendor/github.com/olekukonko/ll/lx/interface.go new file mode 100644 index 0000000000..8d1c5339e9 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lx/interface.go @@ -0,0 +1,67 @@ +package lx + +import "io" + +// Handler defines the interface for processing log entries. +// Implementations (e.g., TextHandler, JSONHandler) format and output log entries to various +// destinations (e.g., stdout, files). The Handle method returns an error if processing fails, +// allowing the logger to handle output failures gracefully. +// Example (simplified handler implementation): +// +// type MyHandler struct{} +// func (h *MyHandler) Handle(e *Entry) error { +// fmt.Printf("[%s] %s: %s\n", e.Namespace, e.Level.String(), e.Message) +// return nil +// } +type Handler interface { + Handle(e *Entry) error // Processes a log entry, returning any error +} + +// Outputter defines the interface for handlers that support dynamic output +// destination changes. Implementations can switch their output writer at runtime. +// +// Example usage: +// +// h := &JSONHandler{} +// h.Output(os.Stderr) // Switch to stderr +// h.Output(file) // Switch to file +type Outputter interface { + Output(w io.Writer) +} + +// HandlerOutputter combines the Handler and Outputter interfaces. +// Types implementing this interface can both process log entries and +// dynamically change their output destination at runtime. +// +// This is useful for creating flexible logging handlers that support +// features like log rotation, output redirection, or runtime configuration. +// +// Example usage: +// +// var ho HandlerOutputter = &TextHandler{} +// // Handle log entries +// ho.Handle(&Entry{...}) +// // Switch output destination +// ho.Output(os.Stderr) +// +// Common implementations include TextHandler and JSONHandler when they +// support output destination changes. +type HandlerOutputter interface { + Handler // Can process log entries + Outputter // Can change output destination (has Output(w io.Writer) method) +} + +// Timestamper defines an interface for handlers that support timestamp configuration. +// It includes a method to enable or disable timestamp logging and optionally set the timestamp format. +type Timestamper interface { + // Timestamped enables or disables timestamp logging and allows specifying an optional format. + // Parameters: + // enable: Boolean to enable or disable timestamp logging + // format: Optional string(s) to specify the timestamp format + Timestamped(enable bool, format ...string) +} + +// Wrap is a handler decorator function that transforms a log handler. +// It takes an existing handler as input and returns a new, wrapped handler +// that adds functionality (like filtering, transformation, or routing). +type Wrap func(next Handler) Handler diff --git a/vendor/github.com/olekukonko/ll/lx/lx.go b/vendor/github.com/olekukonko/ll/lx/lx.go index ded0e58b26..f0b8743c9f 100644 --- a/vendor/github.com/olekukonko/ll/lx/lx.go +++ b/vendor/github.com/olekukonko/ll/lx/lx.go @@ -1,10 +1,5 @@ package lx -import ( - "strings" - "time" -) - // Formatting constants for log output. // These constants define the characters used to format log messages, ensuring consistency // across handlers (e.g., text, JSON, colorized). They are used to construct namespace paths, @@ -16,7 +11,7 @@ const ( Arrow = "→" // Arrow for NestedPath style namespaces (e.g., [parent]→[child]) LeftBracket = "[" // Opening bracket for namespaces and fields (e.g., [app]) RightBracket = "]" // Closing bracket for namespaces and fields (e.g., [app]) - Colon = ":" // Separator after namespace or level (e.g., [app]: INFO:) + Colon = ":" // Separator after namespace or level (e.g., [app]: INFO:) can also be "|" Dot = "." // Separator for namespace paths (e.g., "parent.child") Newline = "\n" // Newline for separating log entries or stack trace lines ) @@ -25,7 +20,7 @@ const ( // It specifies whether logging is enabled by default for new Logger instances in the ll package. // Set to false to prevent logging until explicitly enabled. const ( - DefaultEnabled = false // Default state for new loggers (disabled) + DefaultEnabled = true // Default state for new loggers (disabled) ) // Log level constants, ordered by increasing severity. @@ -57,6 +52,9 @@ const ( DumpString = "DUMP" SpecialString = "SPECIAL" RawString = "RAW" + InspectString = "INSPECT" + DbgString = "DBG" + TimedString = "TIMED" ) // Log class constants, defining the type of log entry. @@ -68,7 +66,10 @@ const ( ClassDump // Dump entries for hex/ASCII dumps ClassSpecial // Special entries for custom or non-standard logs ClassRaw // Raw entries for unformatted output - ClassUnknown // Raw entries for unformatted output + ClassInspect // Inspect entries for debugging + ClassDbg // Inspect entries for debugging + ClassTimed // Inspect entries for debugging + ClassUnknown // Unknown output ) // Namespace style constants. @@ -78,151 +79,3 @@ const ( FlatPath StyleType = iota // Formats namespaces as [parent/child] NestedPath // Formats namespaces as [parent]→[child] ) - -// LevelType represents the severity of a log message. -// It is an integer type used to define log levels (Debug, Info, Warn, Error, None), with associated -// string representations for display in log output. -type LevelType int - -// String converts a LevelType to its string representation. -// It maps each level constant to a human-readable string, returning "UNKNOWN" for invalid levels. -// Used by handlers to display the log level in output. -// Example: -// -// var level lx.LevelType = lx.LevelInfo -// fmt.Println(level.String()) // Output: INFO -func (l LevelType) String() string { - switch l { - case LevelDebug: - return DebugString - case LevelInfo: - return InfoString - case LevelWarn: - return WarnString - case LevelError: - return ErrorString - case LevelFatal: - return FatalString - case LevelNone: - return NoneString - default: - return UnknownString - } -} - -// LevelParse converts a string to its corresponding LevelType. -// It parses a string (case-insensitive) and returns the corresponding LevelType, defaulting to -// LevelUnknown for unrecognized strings. Supports "WARNING" as an alias for "WARN". -func LevelParse(s string) LevelType { - switch strings.ToUpper(s) { - case DebugString: - return LevelDebug - case InfoString: - return LevelInfo - case WarnString, WarningString: // Allow both "WARN" and "WARNING" - return LevelWarn - case ErrorString: - return LevelError - case NoneString: - return LevelNone - default: - return LevelUnknown - } -} - -// StyleType defines how namespace paths are formatted in log output. -// It is an integer type used to select between FlatPath ([parent/child]) and NestedPath -// ([parent]→[child]) styles, affecting how handlers render namespace hierarchies. -type StyleType int - -// Entry represents a single log entry passed to handlers. -// It encapsulates all information about a log message, including its timestamp, severity, -// content, namespace, metadata, and formatting style. Handlers process Entry instances -// to produce formatted output (e.g., text, JSON). The struct is immutable once created, -// ensuring thread-safety in handler processing. -type Entry struct { - Timestamp time.Time // Time the log was created - Level LevelType // Severity level of the log (Debug, Info, Warn, Error, None) - Message string // Log message content - Namespace string // Namespace path (e.g., "parent/child") - Fields map[string]interface{} // Additional key-value metadata (e.g., {"user": "alice"}) - Style StyleType // Namespace formatting style (FlatPath or NestedPath) - Error error // Associated error, if any (e.g., for error logs) - Class ClassType // Type of log entry (Text, JSON, Dump, Special, Raw) - Stack []byte // Stack trace data (if present) - Id int `json:"-"` // Unique ID for the entry, ignored in JSON output -} - -// Handler defines the interface for processing log entries. -// Implementations (e.g., TextHandler, JSONHandler) format and output log entries to various -// destinations (e.g., stdout, files). The Handle method returns an error if processing fails, -// allowing the logger to handle output failures gracefully. -// Example (simplified handler implementation): -// -// type MyHandler struct{} -// func (h *MyHandler) Handle(e *Entry) error { -// fmt.Printf("[%s] %s: %s\n", e.Namespace, e.Level.String(), e.Message) -// return nil -// } -type Handler interface { - Handle(e *Entry) error // Processes a log entry, returning any error -} - -// Timestamper defines an interface for handlers that support timestamp configuration. -// It includes a method to enable or disable timestamp logging and optionally set the timestamp format. -type Timestamper interface { - // Timestamped enables or disables timestamp logging and allows specifying an optional format. - // Parameters: - // enable: Boolean to enable or disable timestamp logging - // format: Optional string(s) to specify the timestamp format - Timestamped(enable bool, format ...string) -} - -// ClassType represents the type of a log entry. -// It is an integer type used to categorize log entries (Text, JSON, Dump, Special, Raw), -// influencing how handlers process and format them. -type ClassType int - -// String converts a ClassType to its string representation. -// It maps each class constant to a human-readable string, returning "UNKNOWN" for invalid classes. -// Used by handlers to indicate the entry type in output (e.g., JSON fields). -// Example: -// -// var class lx.ClassType = lx.ClassText -// fmt.Println(class.String()) // Output: TEST -func (t ClassType) String() string { - switch t { - case ClassText: - return TextString - case ClassJSON: - return JSONString - case ClassDump: - return DumpString - case ClassSpecial: - return SpecialString - case ClassRaw: - return RawString - default: - return UnknownString - } -} - -// ParseClass converts a string to its corresponding ClassType. -// It parses a string (case-insensitive) and returns the corresponding ClassType, defaulting to -// ClassUnknown for unrecognized strings. -func ParseClass(s string) ClassType { - switch strings.ToUpper(s) { - case TextString: - return ClassText - case JSONString: - return ClassJSON - case DumpString: - return ClassDump - case SpecialString: - return ClassSpecial - case RawString: - return ClassRaw - default: - return ClassUnknown - } -} diff --git a/vendor/github.com/olekukonko/ll/lx/ns.go b/vendor/github.com/olekukonko/ll/lx/namespace.go similarity index 100% rename from vendor/github.com/olekukonko/ll/lx/ns.go rename to vendor/github.com/olekukonko/ll/lx/namespace.go diff --git a/vendor/github.com/olekukonko/ll/lx/types.go b/vendor/github.com/olekukonko/ll/lx/types.go new file mode 100644 index 0000000000..58013fa2b5 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/lx/types.go @@ -0,0 +1,144 @@ +package lx + +import ( + "strings" + "time" +) + +// LevelType represents the severity of a log message. +// It is an integer type used to define log levels (Debug, Info, Warn, Error, None), with associated +// string representations for display in log output. +type LevelType int + +// String converts a LevelType to its string representation. +// It maps each level constant to a human-readable string, returning "UNKNOWN" for invalid levels. +// Used by handlers to display the log level in output. +// Example: +// +// var level lx.LevelType = lx.LevelInfo +// fmt.Println(level.String()) // Output: INFO +func (l LevelType) String() string { + switch l { + case LevelDebug: + return DebugString + case LevelInfo: + return InfoString + case LevelWarn: + return WarnString + case LevelError: + return ErrorString + case LevelFatal: + return FatalString + case LevelNone: + return NoneString + default: + return UnknownString + } +} + +func (l LevelType) Name(class ClassType) string { + if class == ClassRaw || class == ClassDump || class == ClassInspect || class == ClassDbg || class == ClassTimed { + return class.String() + } + return l.String() +} + +// LevelParse converts a string to its corresponding LevelType. +// It parses a string (case-insensitive) and returns the corresponding LevelType, defaulting to +// LevelUnknown for unrecognized strings. Supports "WARNING" as an alias for "WARN". +func LevelParse(s string) LevelType { + switch strings.ToUpper(s) { + case DebugString: + return LevelDebug + case InfoString: + return LevelInfo + case WarnString, WarningString: // Allow both "WARN" and "WARNING" + return LevelWarn + case ErrorString: + return LevelError + case NoneString: + return LevelNone + default: + return LevelUnknown + } +} + +// Entry represents a single log entry passed to handlers. +// It encapsulates all information about a log message, including its timestamp, severity, +// content, namespace, metadata, and formatting style. Handlers process Entry instances +// to produce formatted output (e.g., text, JSON). The struct is immutable once created, +// ensuring thread-safety in handler processing. +type Entry struct { + Timestamp time.Time // Time the log was created + Level LevelType // Severity level of the log (Debug, Info, Warn, Error, None) + Message string // Log message content + Namespace string // Namespace path (e.g., "parent/child") + Fields Fields // Additional key-value metadata (e.g., {"user": "alice"}) + Style StyleType // Namespace formatting style (FlatPath or NestedPath) + Error error // Associated error, if any (e.g., for error logs) + Class ClassType // Type of log entry (Text, JSON, Dump, Special, Raw) + Stack []byte // Stack trace data (if present) + Id int `json:"-"` // Unique ID for the entry, ignored in JSON output +} + +// StyleType defines how namespace paths are formatted in log output. +// It is an integer type used to select between FlatPath ([parent/child]) and NestedPath +// ([parent]→[child]) styles, affecting how handlers render namespace hierarchies. +type StyleType int + +// ClassType represents the type of a log entry. +// It is an integer type used to categorize log entries (Text, JSON, Dump, Special, Raw), +// influencing how handlers process and format them. +type ClassType int + +// String converts a ClassType to its string representation. +// It maps each class constant to a human-readable string, returning "UNKNOWN" for invalid classes. +// Used by handlers to indicate the entry type in output (e.g., JSON fields). +// Example: +// +// var class lx.ClassType = lx.ClassText +// fmt.Println(class.String()) // Output: TEST +func (t ClassType) String() string { + switch t { + case ClassText: + + return TextString + case ClassJSON: + + return JSONString + case ClassDump: + return DumpString + case ClassSpecial: + return SpecialString + case ClassInspect: + return InspectString + case ClassDbg: + return DbgString + case ClassRaw: + return RawString + case ClassTimed: + return TimedString + default: + return UnknownString + } +} + +// ParseClass converts a string to its corresponding ClassType. +// It parses a string (case-insensitive) and returns the corresponding ClassType, defaulting to +// ClassUnknown for unrecognized strings. +func ParseClass(s string) ClassType { + switch strings.ToUpper(s) { + case TextString: + return ClassText + case JSONString: + return ClassJSON + case DumpString: + return ClassDump + case SpecialString: + return ClassSpecial + case RawString: + return ClassRaw + default: + return ClassUnknown + } +} diff --git a/vendor/github.com/olekukonko/ll/since.go b/vendor/github.com/olekukonko/ll/since.go new file mode 100644 index 0000000000..22be568a27 --- /dev/null +++ b/vendor/github.com/olekukonko/ll/since.go @@ -0,0 +1,388 @@ +package ll + +import ( + "fmt" + "strings" + "time" + + "github.com/olekukonko/ll/lx" +) + +// Measure executes one or more functions and logs the duration of each. +// It returns the total cumulative duration across all functions. +// +// Each function in `fns` is run sequentially. If a function is `nil`, it is skipped. +// +// Optional labels previously set via `Labels(...)` are applied to the corresponding function +// by position. If there are fewer labels than functions, missing labels are replaced with +// default names like "fn_0", "fn_1", etc. Labels are cleared after the call to prevent reuse. +// +// Example usage: +// +// logger := New("app").Enable() +// +// // Optional: add labels for functions +// logger.Labels("load_users", "process_orders") +// +// total := logger.Measure( +// func() { +// // simulate work 1 +// time.Sleep(100 * time.Millisecond) +// }, +// func() { +// // simulate work 2 +// time.Sleep(200 * time.Millisecond) +// }, +// func() { +// // simulate work 3 +// time.Sleep(50 * time.Millisecond) +// }, +// ) +// +// // Logs something like: +// // [load_users] completed duration=100ms +// // [process_orders] completed duration=200ms +// // [fn_2] completed duration=50ms +// +// Returns the sum of durations of all executed functions. +func (l *Logger) Measure(fns ...func()) time.Duration { + if len(fns) == 0 { + return 0 + } + + var total time.Duration + lblPtr := l.labels.Swap(nil) + var lbls []string + if lblPtr != nil { + lbls = *lblPtr + } + + for i, fn := range fns { + if fn == nil { + continue + } + // Use SinceBuilder instead of manual timing + sb := l.Since() // starts timer internally + fn() + duration := sb.Fields( + "index", i, + ).Info(fmt.Sprintf("[%s] completed", func() string { + if i < len(lbls) && lbls[i] != "" { + return lbls[i] + } + return fmt.Sprintf("fn_%d", i) + }())) + + total += duration + } + + return total +} + +// Since creates a timer that will log the duration when completed +// If startTime is provided, uses that as the start time; otherwise uses time.Now() +// +// defer logger.Since().Info("request") // Auto-start +// logger.Since(start).Info("request") // Manual timing +// logger.Since().If(debug).Debug("timing") // Conditional +func (l *Logger) Since(startTime ...time.Time) *SinceBuilder { + start := time.Now() + if len(startTime) > 0 && !startTime[0].IsZero() { + start = startTime[0] + } + + return &SinceBuilder{ + logger: l, + start: start, + condition: true, + fields: nil, // Lazily initialized + } +} + +// SinceBuilder provides a fluent API for logging timed operations +// It mirrors FieldBuilder exactly for field operations +type SinceBuilder struct { + logger *Logger + start time.Time + condition bool + fields lx.Fields +} + +// --------------------------------------------------------------------- +// Conditional Methods (match conditional.go pattern) +// --------------------------------------------------------------------- + +// If adds a condition to this timer - only logs if condition is true +func (sb *SinceBuilder) If(condition bool) *SinceBuilder { + sb.condition = sb.condition && condition + return sb +} + +// IfErr adds an error condition - only logs if err != nil +func (sb *SinceBuilder) IfErr(err error) *SinceBuilder { + sb.condition = sb.condition && (err != nil) + return sb +} + +// IfAny logs if ANY condition is true +func (sb *SinceBuilder) IfAny(conditions ...bool) *SinceBuilder { + if !sb.condition { + return sb + } + + for _, cond := range conditions { + if cond { + return sb + } + } + sb.condition = false + return sb +} + +// IfOne logs if ALL conditions are true +func (sb *SinceBuilder) IfOne(conditions ...bool) *SinceBuilder { + if !sb.condition { + return sb + } + + for _, cond := range conditions { + if !cond { + sb.condition = false + return sb + } + } + return sb +} + +// --------------------------------------------------------------------- +// Field Methods - EXACT MATCH with FieldBuilder API +// --------------------------------------------------------------------- + +// Fields adds key-value pairs as fields (variadic) +// EXACT match to FieldBuilder.Fields() +func (sb *SinceBuilder) Fields(pairs ...any) *SinceBuilder { + if sb.logger.suspend.Load() || !sb.condition { + return sb + } + + // Lazy initialization + if sb.fields == nil { + sb.fields = make(lx.Fields, 0, len(pairs)/2) + } + + // Process key-value pairs + for i := 0; i < len(pairs)-1; i += 2 { + if key, ok := pairs[i].(string); ok { + sb.fields = append(sb.fields, lx.Field{Key: key, Value: pairs[i+1]}) + } else { + // Log error for non-string keys (matches Fields behavior) + sb.fields = append(sb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("missing key '%v'", pairs[i]), + }) + } + } + + // Handle uneven pairs (matches Fields behavior) + if len(pairs)%2 != 0 { + sb.fields = append(sb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("missing key '%v'", pairs[len(pairs)-1]), + }) + } + + return sb +} + +// Field adds fields from a map +// EXACT match to FieldBuilder.Field() +func (sb *SinceBuilder) Field(fields map[string]interface{}) *SinceBuilder { + if sb.logger.suspend.Load() || !sb.condition || len(fields) == 0 { + return sb + } + + // Lazy initialization + if sb.fields == nil { + sb.fields = make(lx.Fields, 0, len(fields)) + } + + // Copy fields from input map (preserves iteration order) + for k, v := range fields { + sb.fields = append(sb.fields, lx.Field{Key: k, Value: v}) + } + + return sb +} + +// Err adds one or more errors as a field +// EXACT match to FieldBuilder.Err() +func (sb *SinceBuilder) Err(errs ...error) *SinceBuilder { + if sb.logger.suspend.Load() || !sb.condition { + return sb + } + + // Lazy initialization + if sb.fields == nil { + sb.fields = make(lx.Fields, 0, 2) + } + + // Collect non-nil errors + var nonNilErrors []error + var builder strings.Builder + count := 0 + + for i, err := range errs { + if err != nil { + if i > 0 && count > 0 { + builder.WriteString("; ") + } + builder.WriteString(err.Error()) + nonNilErrors = append(nonNilErrors, err) + count++ + } + } + + if count > 0 { + if count == 1 { + sb.fields = append(sb.fields, lx.Field{Key: "error", Value: nonNilErrors[0]}) + } else { + sb.fields = append(sb.fields, lx.Field{Key: "error", Value: nonNilErrors}) + } + // Note: Unlike FieldBuilder.Err(), we DON'T log immediately + // The error will be included in the timing log + } + + return sb +} + +// Merge adds additional key-value pairs to the fields +// EXACT match to FieldBuilder.Merge() +func (sb *SinceBuilder) Merge(pairs ...any) *SinceBuilder { + if sb.logger.suspend.Load() || !sb.condition { + return sb + } + + // Lazy initialization + if sb.fields == nil { + sb.fields = make(lx.Fields, 0, len(pairs)/2) + } + + // Process pairs as key-value + for i := 0; i < len(pairs)-1; i += 2 { + if key, ok := pairs[i].(string); ok { + sb.fields = append(sb.fields, lx.Field{Key: key, Value: pairs[i+1]}) + } else { + sb.fields = append(sb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("non-string key in Merge: %v", pairs[i]), + }) + } + } + + if len(pairs)%2 != 0 { + sb.fields = append(sb.fields, lx.Field{ + Key: "error", + Value: fmt.Errorf("uneven key-value pairs in Merge: [%v]", pairs[len(pairs)-1]), + }) + } + + return sb +} + +// --------------------------------------------------------------------- +// Logging Methods (match logger pattern) +// --------------------------------------------------------------------- + +// Debug logs the duration at Debug level with message +func (sb *SinceBuilder) Debug(msg string) time.Duration { + return sb.logAtLevel(lx.LevelDebug, msg) +} + +// Info logs the duration at Info level with message +func (sb *SinceBuilder) Info(msg string) time.Duration { + return sb.logAtLevel(lx.LevelInfo, msg) +} + +// Warn logs the duration at Warn level with message +func (sb *SinceBuilder) Warn(msg string) time.Duration { + return sb.logAtLevel(lx.LevelWarn, msg) +} + +// Error logs the duration at Error level with message +func (sb *SinceBuilder) Error(msg string) time.Duration { + return sb.logAtLevel(lx.LevelError, msg) +} + +// Log is an alias for Info (for backward compatibility) +func (sb *SinceBuilder) Log(msg string) time.Duration { + return sb.Info(msg) +} + +// logAtLevel internal method that handles the actual logging +func (sb *SinceBuilder) logAtLevel(level lx.LevelType, msg string) time.Duration { + // Fast path - don't even compute duration if we're not logging + if !sb.condition || sb.logger.suspend.Load() || !sb.logger.shouldLog(level) { + return time.Since(sb.start) + } + + duration := time.Since(sb.start) + + // Build final fields in this order: + // 1. Logger context fields (from logger.context) + // 2. Builder fields (from sb.fields) + // 3. Duration fields (always last) + + // Pre-allocate with exact capacity + totalFields := 0 + if sb.logger.context != nil { + totalFields += len(sb.logger.context) + } + if sb.fields != nil { + totalFields += len(sb.fields) + } + totalFields += 2 // duration_ms, duration + + fields := make(lx.Fields, 0, totalFields) + + // Add logger context fields first (preserves order) + if sb.logger.context != nil { + fields = append(fields, sb.logger.context...) + } + + // Add builder fields + if sb.fields != nil { + fields = append(fields, sb.fields...) + } + + // Add duration fields last (so they're visible at the end) + fields = append(fields, + lx.Field{Key: "duration_ms", Value: duration.Milliseconds()}, + lx.Field{Key: "duration", Value: duration.String()}, + ) + + sb.logger.log(level, lx.ClassTimed, msg, fields, false) + return duration +} + +// --------------------------------------------------------------------- +// Utility Methods +// --------------------------------------------------------------------- + +// Reset allows reusing the builder with a new start time +// Zero-allocation - keeps fields slice capacity +func (sb *SinceBuilder) Reset(startTime ...time.Time) *SinceBuilder { + sb.start = time.Now() + if len(startTime) > 0 && !startTime[0].IsZero() { + sb.start = startTime[0] + } + sb.condition = true + if sb.fields != nil { + sb.fields = sb.fields[:0] // Keep capacity, zero length + } + return sb +} + +// Elapsed returns the current duration without logging +func (sb *SinceBuilder) Elapsed() time.Duration { + return time.Since(sb.start) +} diff --git a/vendor/github.com/olekukonko/tablewriter/MIGRATION.md b/vendor/github.com/olekukonko/tablewriter/MIGRATION.md index 08ab3055c3..057901dd82 100644 --- a/vendor/github.com/olekukonko/tablewriter/MIGRATION.md +++ b/vendor/github.com/olekukonko/tablewriter/MIGRATION.md @@ -448,7 +448,7 @@ func NewInvoiceRenderer() *InvoiceRenderer { Settings: tw.Settings{Separators: tw.SeparatorsNone, Lines: tw.LinesNone}, Streaming: false, } - defaultLogger := ll.New("simple-invoice-renderer") + defaultLogger := ll.New("simple-invoice-renderer").Disable() return &InvoiceRenderer{logger: defaultLogger, rendition: rendition} } diff --git a/vendor/github.com/olekukonko/tablewriter/benchstat.txt b/vendor/github.com/olekukonko/tablewriter/benchstat.txt deleted file mode 100644 index 912c38deb3..0000000000 --- a/vendor/github.com/olekukonko/tablewriter/benchstat.txt +++ /dev/null @@ -1,194 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/olekukonko/tablewriter/pkg/twwarp -cpu: Apple M2 - │ old.txt │ new.txt │ - │ sec/op │ sec/op vs base │ -WrapString-8 112.8µ ± 1% 112.9µ ± 2% ~ (p=0.589 n=6) -WrapStringWithSpaces-8 113.4µ ± 1% 113.7µ ± 1% ~ (p=0.310 n=6) -geomean 113.1µ 113.3µ +0.15% - - │ old.txt │ new.txt │ - │ B/s │ B/s vs base │ -WrapString-8 84.92Mi ± 1% 84.82Mi ± 2% ~ (p=0.589 n=6) -WrapStringWithSpaces-8 84.43Mi ± 1% 84.27Mi ± 1% ~ (p=0.310 n=6) -geomean 84.68Mi 84.55Mi -0.15% - - │ old.txt │ new.txt │ - │ B/op │ B/op vs base │ -WrapString-8 47.35Ki ± 0% 47.35Ki ± 0% ~ (p=1.000 n=6) ¹ -WrapStringWithSpaces-8 52.76Ki ± 0% 52.76Ki ± 0% ~ (p=1.000 n=6) ¹ -geomean 49.98Ki 49.98Ki +0.00% -¹ all samples are equal - - │ old.txt │ new.txt │ - │ allocs/op │ allocs/op vs base │ -WrapString-8 33.00 ± 0% 33.00 ± 0% ~ (p=1.000 n=6) ¹ -WrapStringWithSpaces-8 51.00 ± 0% 51.00 ± 0% ~ (p=1.000 n=6) ¹ -geomean 41.02 41.02 +0.00% -¹ all samples are equal - -pkg: github.com/olekukonko/tablewriter/pkg/twwidth - │ old.txt │ new.txt │ - │ sec/op │ sec/op vs base │ -WidthFunction/SimpleASCII_EAfalse_NoCache-8 387.6n ± 1% 368.4n ± 2% -4.97% (p=0.002 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 219.0n ± 127% 217.5n ± 119% ~ (p=0.372 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheHit-8 14.78n ± 1% 14.54n ± 3% ~ (p=0.061 n=6) -WidthFunction/SimpleASCII_EAtrue_NoCache-8 676.4n ± 1% 366.8n ± 2% -45.77% (p=0.002 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 216.1n ± 375% 216.0n ± 128% ~ (p=0.937 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheHit-8 14.71n ± 0% 14.49n ± 0% -1.53% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1.027µ ± 3% 1.007µ ± 1% -2.00% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 219.5n ± 516% 221.4n ± 502% ~ (p=0.515 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 14.81n ± 1% 14.61n ± 1% -1.35% (p=0.009 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1.313µ ± 2% 1.009µ ± 2% -23.15% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 653.2n ± 150% 218.2n ± 524% ~ (p=0.331 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 14.73n ± 2% 14.50n ± 0% -1.60% (p=0.002 n=6) -WidthFunction/EastAsian_EAfalse_NoCache-8 747.3n ± 1% 336.2n ± 1% -55.02% (p=0.002 n=6) -WidthFunction/EastAsian_EAfalse_CacheMiss-8 226.3n ± 384% 227.4n ± 113% ~ (p=0.937 n=6) -WidthFunction/EastAsian_EAfalse_CacheHit-8 14.74n ± 1% 14.58n ± 1% -1.09% (p=0.011 n=6) -WidthFunction/EastAsian_EAtrue_NoCache-8 965.4n ± 2% 348.7n ± 0% -63.88% (p=0.002 n=6) -WidthFunction/EastAsian_EAtrue_CacheMiss-8 225.4n ± 511% 225.8n ± 111% ~ (p=1.000 n=6) -WidthFunction/EastAsian_EAtrue_CacheHit-8 14.72n ± 1% 14.54n ± 3% ~ (p=0.056 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1376.0n ± 2% 983.8n ± 2% -28.50% (p=0.002 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 633.6n ± 170% 222.4n ± 513% ~ (p=0.974 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 15.73n ± 1% 15.64n ± 1% ~ (p=0.227 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1589.5n ± 1% 996.9n ± 2% -37.29% (p=0.002 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 484.8n ± 309% 221.3n ± 516% ~ (p=0.240 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 15.74n ± 1% 15.73n ± 1% ~ (p=0.485 n=6) -WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 4.916µ ± 3% 4.512µ ± 4% -8.22% (p=0.002 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 2.430µ ± 114% 2.182µ ± 123% ~ (p=0.699 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 23.75n ± 3% 23.24n ± 3% ~ (p=0.065 n=6) -WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 9.273µ ± 1% 4.519µ ± 1% -51.27% (p=0.002 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 4.021µ ± 131% 2.127µ ± 128% ~ (p=0.240 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 23.50n ± 2% 23.48n ± 1% ~ (p=0.589 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 57.36µ ± 1% 57.33µ ± 2% ~ (p=0.818 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 22.18µ ± 135% 14.55µ ± 299% ~ (p=0.589 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 44.21n ± 1% 44.20n ± 2% ~ (p=0.818 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 60.25µ ± 2% 57.90µ ± 2% -3.90% (p=0.002 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 16.11µ ± 263% 20.02µ ± 183% ~ (p=0.699 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 44.57n ± 1% 44.18n ± 2% ~ (p=0.461 n=6) -geomean 358.5n 283.9n -20.82% - - │ old.txt │ new.txt │ - │ B/s │ B/s vs base │ -WidthFunction/SimpleASCII_EAfalse_NoCache-8 86.11Mi ± 1% 90.63Mi ± 2% +5.24% (p=0.002 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 152.4Mi ± 56% 153.5Mi ± 54% ~ (p=0.394 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheHit-8 2.205Gi ± 1% 2.242Gi ± 3% ~ (p=0.065 n=6) -WidthFunction/SimpleASCII_EAtrue_NoCache-8 49.35Mi ± 1% 91.00Mi ± 2% +84.40% (p=0.002 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 154.5Mi ± 79% 154.5Mi ± 56% ~ (p=0.937 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheHit-8 2.215Gi ± 0% 2.250Gi ± 0% +1.58% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 56.66Mi ± 2% 57.78Mi ± 1% +1.99% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 265.1Mi ± 84% 262.7Mi ± 83% ~ (p=0.485 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 3.836Gi ± 1% 3.888Gi ± 1% +1.34% (p=0.009 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 44.30Mi ± 2% 57.65Mi ± 2% +30.14% (p=0.002 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 147.3Mi ± 81% 266.7Mi ± 84% ~ (p=0.310 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 3.856Gi ± 2% 3.919Gi ± 0% +1.63% (p=0.002 n=6) -WidthFunction/EastAsian_EAfalse_NoCache-8 76.58Mi ± 1% 170.21Mi ± 1% +122.28% (p=0.002 n=6) -WidthFunction/EastAsian_EAfalse_CacheMiss-8 252.8Mi ± 79% 251.6Mi ± 53% ~ (p=0.937 n=6) -WidthFunction/EastAsian_EAfalse_CacheHit-8 3.791Gi ± 1% 3.832Gi ± 1% +1.08% (p=0.009 n=6) -WidthFunction/EastAsian_EAtrue_NoCache-8 59.27Mi ± 2% 164.10Mi ± 0% +176.87% (p=0.002 n=6) -WidthFunction/EastAsian_EAtrue_CacheMiss-8 253.9Mi ± 84% 253.4Mi ± 53% ~ (p=1.000 n=6) -WidthFunction/EastAsian_EAtrue_CacheHit-8 3.796Gi ± 1% 3.841Gi ± 3% ~ (p=0.065 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 60.29Mi ± 1% 84.33Mi ± 2% +39.88% (p=0.002 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 227.1Mi ± 79% 373.2Mi ± 84% ~ (p=1.000 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 5.154Gi ± 1% 5.181Gi ± 1% ~ (p=0.240 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 52.19Mi ± 1% 83.23Mi ± 2% +59.47% (p=0.002 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 230.9Mi ± 82% 374.9Mi ± 84% ~ (p=0.240 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 5.147Gi ± 1% 5.152Gi ± 1% ~ (p=0.485 n=6) -WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 104.8Mi ± 3% 114.1Mi ± 4% +8.95% (p=0.002 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 368.0Mi ± 293% 474.3Mi ± 211% ~ (p=0.699 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 21.17Gi ± 3% 21.64Gi ± 2% ~ (p=0.065 n=6) -WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 55.54Mi ± 1% 113.97Mi ± 1% +105.21% (p=0.002 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 399.8Mi ± 232% 577.5Mi ± 149% ~ (p=0.240 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 21.40Gi ± 2% 21.41Gi ± 1% ~ (p=0.589 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 34.08Mi ± 1% 34.10Mi ± 2% ~ (p=0.784 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 101.5Mi ± 1396% 643.9Mi ± 320% ~ (p=0.589 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 43.18Gi ± 1% 43.20Gi ± 2% ~ (p=0.818 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 32.45Mi ± 2% 33.76Mi ± 2% +4.06% (p=0.002 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 393.0Mi ± 296% 122.4Mi ± 1610% ~ (p=0.699 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 42.83Gi ± 1% 43.21Gi ± 2% ~ (p=0.485 n=6) -geomean 456.4Mi 560.6Mi +22.83% - - │ old.txt │ new.txt │ - │ B/op │ B/op vs base │ -WidthFunction/SimpleASCII_EAfalse_NoCache-8 112.0 ± 1% 113.0 ± 0% ~ (p=0.061 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 55.00 ± 200% 55.00 ± 202% ~ (p=1.000 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/SimpleASCII_EAtrue_NoCache-8 113.0 ± 1% 113.0 ± 0% ~ (p=1.000 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 55.00 ± 505% 55.00 ± 205% ~ (p=0.697 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 185.0 ± 0% 185.0 ± 1% ~ (p=0.455 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 87.00 ± 402% 87.00 ± 401% ~ (p=1.000 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 185.0 ± 0% 185.0 ± 1% ~ (p=1.000 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 174.00 ± 115% 87.00 ± 401% ~ (p=0.621 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAfalse_NoCache-8 145.0 ± 0% 146.0 ± 0% +0.69% (p=0.002 n=6) -WidthFunction/EastAsian_EAfalse_CacheMiss-8 87.00 ± 392% 87.00 ± 167% ~ (p=0.697 n=6) -WidthFunction/EastAsian_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAtrue_NoCache-8 145.0 ± 1% 146.0 ± 1% +0.69% (p=0.013 n=6) -WidthFunction/EastAsian_EAtrue_CacheMiss-8 87.00 ± 392% 87.00 ± 164% ~ (p=0.697 n=6) -WidthFunction/EastAsian_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 193.0 ± 1% 193.0 ± 0% ~ (p=1.000 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 232.0 ± 134% 103.0 ± 485% ~ (p=0.924 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 193.0 ± 0% 193.0 ± 1% ~ (p=1.000 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 185.0 ± 203% 103.0 ± 485% ~ (p=0.621 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 1.153Ki ± 0% 1.150Ki ± 0% ~ (p=0.126 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1.050Ki ± 72% 1.047Ki ± 74% ~ (p=0.939 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 1.152Ki ± 0% 1.155Ki ± 0% +0.30% (p=0.015 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1.036Ki ± 71% 1.039Ki ± 76% ~ (p=0.981 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 1.355Ki ± 0% 1.358Ki ± 0% ~ (p=0.065 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2.787Ki ± 31% 2.613Ki ± 43% ~ (p=0.805 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 1.358Ki ± 0% 1.361Ki ± 0% ~ (p=0.158 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2.625Ki ± 43% 2.741Ki ± 37% ~ (p=0.987 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -geomean ² -5.62% ² -¹ all samples are equal -² summaries must be >0 to compute geomean - - │ old.txt │ new.txt │ - │ allocs/op │ allocs/op vs base │ -WidthFunction/SimpleASCII_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 1.000 ± 200% 1.000 ± 200% ~ (p=1.000 n=6) -WidthFunction/SimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/SimpleASCII_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6) -WidthFunction/SimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1.000 ± 600% 1.000 ± 600% ~ (p=1.000 n=6) -WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 3.500 ± 100% 1.000 ± 600% ~ (p=0.610 n=6) -WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAfalse_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6) -WidthFunction/EastAsian_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsian_EAtrue_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6) -WidthFunction/EastAsian_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 3.000 ± 133% 1.000 ± 600% ~ (p=1.000 n=6) -WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 2.500 ± 180% 1.000 ± 600% ~ (p=0.610 n=6) -WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3.000 ± 67% 3.000 ± 67% ~ (p=1.000 n=6) -WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3.000 ± 67% 3.000 ± 67% ~ (p=1.000 n=6) -WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 9.000 ± 0% 9.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 5.000 ± 100% 3.500 ± 186% ~ (p=0.978 n=6) -WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 9.000 ± 0% 9.000 ± 0% ~ (p=1.000 n=6) ¹ -WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 4.000 ± 150% 4.500 ± 122% ~ (p=0.952 n=6) -WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹ -geomean ² -9.28% ² -¹ all samples are equal -² summaries must be >0 to compute geomean diff --git a/vendor/github.com/olekukonko/tablewriter/comb.hcl b/vendor/github.com/olekukonko/tablewriter/comb.hcl new file mode 100644 index 0000000000..6d5025af2d --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/comb.hcl @@ -0,0 +1,10 @@ +recursive = true +output_file = "all.txt" +extensions = [".go"] +exclude_dirs = [ + "_examples", "_readme", "_lab","_tmp","pkg","lab","cmd","test.txt","tmp", + "_readme","pkg","renderer" +] +exclude_files = ["README.md","README_LEGACY.md","MIGRATION.md","test.hcl","csv.go"] +use_gitignore = true +detailed = true \ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/config.go b/vendor/github.com/olekukonko/tablewriter/config.go index 415c9576bd..6e4038c27d 100644 --- a/vendor/github.com/olekukonko/tablewriter/config.go +++ b/vendor/github.com/olekukonko/tablewriter/config.go @@ -81,6 +81,13 @@ func (b *ConfigBuilder) WithTrimSpace(state tw.State) *ConfigBuilder { return b } +// WithTrimTab enables or disables automatic trimming of leading/trailing tabs. +// Useful for preserving indentation in code blocks while trimming other whitespace. +func (b *ConfigBuilder) WithTrimTab(state tw.State) *ConfigBuilder { + b.config.Behavior.TrimTab = state + return b +} + // WithDebug enables/disables debug logging func (b *ConfigBuilder) WithDebug(debug bool) *ConfigBuilder { b.config.Debug = debug @@ -796,6 +803,12 @@ func (bb *BehaviorConfigBuilder) WithTrimSpace(state tw.State) *BehaviorConfigBu return bb } +// WithTrimTab enables/disables trim tab +func (bb *BehaviorConfigBuilder) WithTrimTab(state tw.State) *BehaviorConfigBuilder { + bb.config.TrimTab = state + return bb +} + // WithHeaderHide enables/disables header visibility func (bb *BehaviorConfigBuilder) WithHeaderHide(state tw.State) *BehaviorConfigBuilder { bb.config.Header.Hide = state diff --git a/vendor/github.com/olekukonko/tablewriter/new.txt b/vendor/github.com/olekukonko/tablewriter/new.txt deleted file mode 100644 index 46791ad1a5..0000000000 --- a/vendor/github.com/olekukonko/tablewriter/new.txt +++ /dev/null @@ -1,248 +0,0 @@ -PASS -ok github.com/olekukonko/tablewriter 0.284s -? github.com/olekukonko/tablewriter/cmd/csv2table [no test files] -goos: darwin -goarch: arm64 -pkg: github.com/olekukonko/tablewriter/pkg/twwarp -cpu: Apple M2 -BenchmarkWrapString-8 10030 114909 ns/op 87.40 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112188 ns/op 89.52 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 113708 ns/op 88.32 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 113233 ns/op 88.69 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112575 ns/op 89.21 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112604 ns/op 89.19 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113731 ns/op 88.30 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113511 ns/op 88.48 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113575 ns/op 88.43 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113746 ns/op 88.29 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113473 ns/op 88.51 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 114487 ns/op 87.72 MB/s 54024 B/op 51 allocs/op -PASS -ok github.com/olekukonko/tablewriter/pkg/twwarp 14.612s -goos: darwin -goarch: arm64 -pkg: github.com/olekukonko/tablewriter/pkg/twwidth -cpu: Apple M2 -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 264374 4533 ns/op 119.12 MB/s 1178 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265746 4514 ns/op 119.62 MB/s 1177 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 263538 4509 ns/op 119.75 MB/s 1178 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 266173 4510 ns/op 119.72 MB/s 1180 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265224 4676 ns/op 115.48 MB/s 1180 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265696 4508 ns/op 119.80 MB/s 1177 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 251047 4859 ns/op 111.13 MB/s 1867 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1000000 3945 ns/op 136.89 MB/s 1584 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3504475 3729 ns/op 144.81 MB/s 1474 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3664098 635.4 ns/op 849.84 MB/s 670 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3818680 588.6 ns/op 917.47 MB/s 667 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3761966 348.7 ns/op 1548.66 MB/s 583 B/op 1 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 49524442 23.54 ns/op 22938.55 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51765230 23.25 ns/op 23221.81 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51881983 23.83 ns/op 22664.79 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51665586 23.20 ns/op 23272.39 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51782077 23.23 ns/op 23250.20 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51498277 23.21 ns/op 23267.21 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 263586 4520 ns/op 119.47 MB/s 1183 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265484 4519 ns/op 119.49 MB/s 1182 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265218 4514 ns/op 119.64 MB/s 1181 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265957 4515 ns/op 119.60 MB/s 1184 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265981 4518 ns/op 119.52 MB/s 1183 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265028 4574 ns/op 118.06 MB/s 1184 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 251682 4853 ns/op 111.27 MB/s 1869 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1000000 3893 ns/op 138.70 MB/s 1583 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3596130 3747 ns/op 144.13 MB/s 1499 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3671358 506.1 ns/op 1066.92 MB/s 628 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3687993 370.6 ns/op 1456.96 MB/s 594 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3672946 358.4 ns/op 1506.88 MB/s 583 B/op 1 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 49266897 23.64 ns/op 22844.78 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 50158659 23.54 ns/op 22938.83 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 50689321 23.45 ns/op 23025.77 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51113672 23.52 ns/op 22954.95 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51489162 23.21 ns/op 23269.51 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51705564 23.16 ns/op 23311.21 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20930 57159 ns/op 35.86 MB/s 1389 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20882 57502 ns/op 35.65 MB/s 1395 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21103 57730 ns/op 35.51 MB/s 1391 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20889 56615 ns/op 36.21 MB/s 1393 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20808 58303 ns/op 35.16 MB/s 1391 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21104 56727 ns/op 36.14 MB/s 1387 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 38569 27485 ns/op 74.59 MB/s 3041 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 58061 ns/op 35.31 MB/s 3835 B/op 10 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2124566 31025 ns/op 66.08 MB/s 3140 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1607 ns/op 1275.74 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1615826 1224 ns/op 1674.27 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1478348 722.9 ns/op 2835.84 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 23989044 44.26 ns/op 46313.25 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27268802 44.13 ns/op 46454.64 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27292006 44.51 ns/op 46054.40 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 24128786 44.99 ns/op 45569.06 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 26858004 44.09 ns/op 46497.43 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27259458 44.05 ns/op 46538.64 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20671 57887 ns/op 35.41 MB/s 1395 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20966 56795 ns/op 36.09 MB/s 1396 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20708 57092 ns/op 35.91 MB/s 1388 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20882 57917 ns/op 35.40 MB/s 1389 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 21244 58013 ns/op 35.34 MB/s 1393 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20854 58122 ns/op 35.27 MB/s 1396 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 38907 30289 ns/op 67.68 MB/s 3066 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 56603 ns/op 36.22 MB/s 3835 B/op 10 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1949059 29030 ns/op 70.62 MB/s 3084 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1479127 933.7 ns/op 2195.47 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2335996 11012 ns/op 186.17 MB/s 2548 B/op 3 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 983864 1169 ns/op 1753.75 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27291516 44.18 ns/op 46398.32 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27220657 44.18 ns/op 46402.04 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27059124 44.91 ns/op 45645.46 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26679783 44.04 ns/op 46551.62 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27244114 44.14 ns/op 46448.19 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27221737 44.61 ns/op 45948.75 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3247359 366.1 ns/op 95.62 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3292773 370.6 ns/op 94.44 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3275070 365.3 ns/op 95.82 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3291489 365.6 ns/op 95.73 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3282121 374.9 ns/op 93.37 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3198205 375.6 ns/op 93.18 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 3092488 419.4 ns/op 83.45 MB/s 152 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6276060 476.4 ns/op 73.46 MB/s 166 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6135336 218.8 ns/op 159.98 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6175833 216.1 ns/op 161.95 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6156606 215.2 ns/op 162.63 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6160923 216.2 ns/op 161.88 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 78655855 15.02 ns/op 2330.76 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 70905223 14.59 ns/op 2398.68 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82255629 14.49 ns/op 2415.75 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82383864 14.48 ns/op 2417.21 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82325931 14.49 ns/op 2415.73 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82426311 14.66 ns/op 2386.73 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3265182 365.8 ns/op 95.68 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3275419 366.3 ns/op 95.56 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3057087 375.3 ns/op 93.26 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3239217 372.6 ns/op 93.94 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3246429 367.3 ns/op 95.29 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3252763 365.3 ns/op 95.80 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 2986195 396.4 ns/op 88.30 MB/s 142 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6487422 493.6 ns/op 70.90 MB/s 168 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6261225 216.1 ns/op 161.99 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6154988 210.7 ns/op 166.13 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6308702 213.8 ns/op 163.69 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6120438 216.0 ns/op 162.05 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82184980 14.47 ns/op 2419.17 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 78985473 14.51 ns/op 2412.95 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82368319 14.47 ns/op 2419.30 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82366668 14.47 ns/op 2418.96 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82104614 14.53 ns/op 2409.59 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82399426 14.53 ns/op 2409.13 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1020 ns/op 59.80 MB/s 186 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1010 ns/op 60.40 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1007 ns/op 60.55 MB/s 186 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.63 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.65 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.63 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1334 ns/op 45.74 MB/s 436 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6892693 1204 ns/op 50.65 MB/s 321 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6433399 221.7 ns/op 275.14 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6323521 221.2 ns/op 275.73 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6000822 218.5 ns/op 279.15 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6329578 220.3 ns/op 276.90 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 80806719 14.65 ns/op 4163.13 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82397774 14.63 ns/op 4169.11 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82794307 14.76 ns/op 4134.15 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82610730 14.59 ns/op 4180.13 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82639170 14.58 ns/op 4183.56 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82560049 14.45 ns/op 4222.53 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1006 ns/op 60.61 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1012 ns/op 60.29 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1030 ns/op 59.25 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1005 ns/op 60.68 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1006 ns/op 60.64 MB/s 186 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1012 ns/op 60.26 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 1000000 1361 ns/op 44.84 MB/s 436 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6967185 1216 ns/op 50.17 MB/s 323 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6413974 219.1 ns/op 278.46 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6381684 216.9 ns/op 281.27 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6383749 216.2 ns/op 282.14 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6360810 217.3 ns/op 280.75 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81573231 14.53 ns/op 4197.28 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82780268 14.47 ns/op 4215.84 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82845276 14.48 ns/op 4212.74 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82545850 14.51 ns/op 4203.96 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82419704 14.49 ns/op 4209.69 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82121707 14.50 ns/op 4206.82 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3552715 336.1 ns/op 178.50 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3551234 335.0 ns/op 179.09 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3588946 338.9 ns/op 177.05 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3577424 338.5 ns/op 177.25 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3554505 335.4 ns/op 178.89 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3575703 336.2 ns/op 178.46 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 2990224 412.6 ns/op 145.42 MB/s 207 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 6066997 484.0 ns/op 123.95 MB/s 232 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5743347 224.3 ns/op 267.49 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5870154 220.6 ns/op 271.92 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5880489 228.0 ns/op 263.14 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5660132 226.8 ns/op 264.52 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81708613 14.54 ns/op 4126.40 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 79903231 14.65 ns/op 4094.56 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 80580853 14.62 ns/op 4103.14 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82036092 14.73 ns/op 4073.52 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 83622964 14.49 ns/op 4139.65 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82724623 14.53 ns/op 4129.78 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3463408 349.4 ns/op 171.71 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3245782 350.0 ns/op 171.41 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3461160 348.3 ns/op 172.28 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3453544 349.1 ns/op 171.87 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3443858 347.0 ns/op 172.92 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3469286 347.4 ns/op 172.72 MB/s 146 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 3050086 428.5 ns/op 140.04 MB/s 213 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5927800 476.0 ns/op 126.05 MB/s 230 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5852149 223.0 ns/op 269.05 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5721747 224.9 ns/op 266.80 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5751147 225.7 ns/op 265.84 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5893626 225.9 ns/op 265.55 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81984477 14.52 ns/op 4132.81 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 79537578 14.59 ns/op 4112.59 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82339353 14.56 ns/op 4119.49 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82286889 14.92 ns/op 4020.68 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82166224 14.53 ns/op 4129.14 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 83084276 14.52 ns/op 4131.45 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1221180 982.5 ns/op 88.55 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1210902 983.5 ns/op 88.46 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1223528 989.3 ns/op 87.94 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1212517 984.1 ns/op 88.40 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1224182 983.5 ns/op 88.46 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1000000 1007 ns/op 86.36 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 999058 1364 ns/op 63.76 MB/s 603 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6682279 1218 ns/op 71.40 MB/s 465 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6339568 220.6 ns/op 394.46 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6226921 222.3 ns/op 391.34 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6264051 221.1 ns/op 393.47 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6234439 222.4 ns/op 391.23 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 75337251 15.64 ns/op 5562.01 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76826634 15.76 ns/op 5521.54 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76836674 15.79 ns/op 5508.81 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76840162 15.64 ns/op 5564.05 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76694060 15.60 ns/op 5577.81 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76737175 15.62 ns/op 5571.56 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1202406 1012 ns/op 85.93 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1000000 1000 ns/op 86.99 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1208559 993.7 ns/op 87.55 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1209415 990.9 ns/op 87.80 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1206118 1020 ns/op 85.33 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1211994 990.6 ns/op 87.82 MB/s 194 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 1000000 1363 ns/op 63.84 MB/s 603 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6504960 1214 ns/op 71.65 MB/s 465 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6349030 220.2 ns/op 395.18 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6183368 220.3 ns/op 394.99 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6240484 220.6 ns/op 394.32 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6280713 222.0 ns/op 391.95 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 69630140 15.77 ns/op 5517.31 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76043014 15.65 ns/op 5559.61 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76239080 15.63 ns/op 5567.94 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75864739 15.88 ns/op 5479.13 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 71286422 15.74 ns/op 5527.29 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75704404 15.71 ns/op 5536.58 MB/s 0 B/op 0 allocs/op -PASS -ok github.com/olekukonko/tablewriter/pkg/twwidth 659.150s -? github.com/olekukonko/tablewriter/renderer [no test files] -PASS -ok github.com/olekukonko/tablewriter/tests 3.025s -PASS -ok github.com/olekukonko/tablewriter/tw 0.283s diff --git a/vendor/github.com/olekukonko/tablewriter/old.txt b/vendor/github.com/olekukonko/tablewriter/old.txt deleted file mode 100644 index f9916ea0f1..0000000000 --- a/vendor/github.com/olekukonko/tablewriter/old.txt +++ /dev/null @@ -1,248 +0,0 @@ -PASS -ok github.com/olekukonko/tablewriter 0.819s -? github.com/olekukonko/tablewriter/cmd/csv2table [no test files] -goos: darwin -goarch: arm64 -pkg: github.com/olekukonko/tablewriter/pkg/twwarp -cpu: Apple M2 -BenchmarkWrapString-8 10630 111320 ns/op 90.22 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112981 ns/op 88.89 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 113419 ns/op 88.55 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112794 ns/op 89.04 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112400 ns/op 89.35 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapString-8 10000 112767 ns/op 89.06 MB/s 48488 B/op 33 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 115098 ns/op 87.26 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113343 ns/op 88.61 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113702 ns/op 88.33 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113547 ns/op 88.45 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113016 ns/op 88.86 MB/s 54024 B/op 51 allocs/op -BenchmarkWrapStringWithSpaces-8 10000 113206 ns/op 88.71 MB/s 54024 B/op 51 allocs/op -PASS -ok github.com/olekukonko/tablewriter/pkg/twwarp 15.179s -goos: darwin -goarch: arm64 -pkg: github.com/olekukonko/tablewriter/pkg/twwidth -cpu: Apple M2 -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 2953855 387.1 ns/op 90.40 MB/s 112 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3095179 387.8 ns/op 90.24 MB/s 112 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3096141 391.0 ns/op 89.51 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3090711 387.2 ns/op 90.40 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3066110 387.4 ns/op 90.35 MB/s 112 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3098689 389.2 ns/op 89.92 MB/s 112 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 3125685 440.9 ns/op 79.39 MB/s 159 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6477175 496.2 ns/op 70.53 MB/s 165 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6019939 217.7 ns/op 160.79 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6231590 219.2 ns/op 159.67 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6245622 216.2 ns/op 161.90 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6109658 218.8 ns/op 159.95 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80977806 14.73 ns/op 2375.87 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80972566 14.76 ns/op 2371.06 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81432532 14.90 ns/op 2348.78 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80644483 14.85 ns/op 2357.10 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81361905 14.79 ns/op 2365.80 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81612987 14.78 ns/op 2368.60 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1777732 682.2 ns/op 51.30 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1778122 672.9 ns/op 52.01 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1779956 674.0 ns/op 51.93 MB/s 112 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1773282 678.7 ns/op 51.57 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1783092 680.2 ns/op 51.46 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1780448 674.0 ns/op 51.93 MB/s 113 B/op 3 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 1000000 1027 ns/op 34.08 MB/s 333 B/op 4 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6891168 958.3 ns/op 36.52 MB/s 227 B/op 4 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6165972 211.7 ns/op 165.30 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6370098 217.4 ns/op 161.02 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6193920 214.8 ns/op 162.92 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6190384 209.4 ns/op 167.16 MB/s 55 B/op 1 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 79747688 14.75 ns/op 2372.71 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 79607492 14.75 ns/op 2372.90 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81634501 14.73 ns/op 2376.30 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81644916 14.70 ns/op 2381.26 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82505884 14.70 ns/op 2380.77 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81840265 14.70 ns/op 2380.34 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1053 ns/op 57.95 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1028 ns/op 59.34 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1029 ns/op 59.27 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1025 ns/op 59.49 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1026 ns/op 59.48 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1025 ns/op 59.54 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1352 ns/op 45.13 MB/s 437 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6619118 1219 ns/op 50.06 MB/s 320 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6486976 221.2 ns/op 275.81 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6508150 217.8 ns/op 280.07 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6487533 217.4 ns/op 280.56 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6243558 216.4 ns/op 281.93 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 80787679 14.90 ns/op 4093.19 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81640521 14.89 ns/op 4097.92 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81596338 14.71 ns/op 4145.47 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81950889 14.84 ns/op 4111.86 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 79321578 14.78 ns/op 4126.88 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81880058 14.75 ns/op 4134.44 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 906406 1313 ns/op 46.44 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 917503 1313 ns/op 46.46 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 915308 1312 ns/op 46.49 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 918404 1312 ns/op 46.51 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 892551 1338 ns/op 45.58 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 915020 1333 ns/op 45.76 MB/s 185 B/op 6 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 791368 1633 ns/op 37.36 MB/s 374 B/op 7 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 2314653 1064 ns/op 57.34 MB/s 265 B/op 5 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6531552 1198 ns/op 50.94 MB/s 258 B/op 5 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6629763 242.5 ns/op 251.57 MB/s 90 B/op 2 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6388215 219.1 ns/op 278.36 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6472197 218.6 ns/op 279.09 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 80704821 14.76 ns/op 4132.33 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82628028 14.70 ns/op 4149.56 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81870517 14.70 ns/op 4148.97 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81944124 14.99 ns/op 4068.84 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81918950 14.70 ns/op 4150.75 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82547270 14.91 ns/op 4092.20 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1604370 749.9 ns/op 80.02 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1610148 749.7 ns/op 80.03 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1585026 744.8 ns/op 80.56 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1615032 749.9 ns/op 80.01 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1614980 743.3 ns/op 80.72 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1609586 741.8 ns/op 80.88 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 1000000 1095 ns/op 54.77 MB/s 428 B/op 4 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 6214893 995.6 ns/op 60.26 MB/s 316 B/op 4 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5702408 224.5 ns/op 267.21 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5712139 220.2 ns/op 272.50 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5783916 228.2 ns/op 262.91 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5713358 224.0 ns/op 267.91 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 78757815 14.92 ns/op 4020.51 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81419875 14.79 ns/op 4057.15 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81656493 14.75 ns/op 4068.12 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81522430 14.73 ns/op 4073.37 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81887037 14.70 ns/op 4080.93 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82019505 14.72 ns/op 4074.99 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1241600 965.5 ns/op 62.14 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1243646 964.8 ns/op 62.19 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1243516 968.1 ns/op 61.98 MB/s 144 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1241917 965.3 ns/op 62.16 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1242903 985.0 ns/op 60.92 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1223456 964.3 ns/op 62.22 MB/s 145 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 1000000 1378 ns/op 43.55 MB/s 428 B/op 4 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 6265657 1229 ns/op 48.84 MB/s 316 B/op 4 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5960497 224.3 ns/op 267.52 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5961004 222.6 ns/op 269.52 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5772004 226.5 ns/op 264.87 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5766748 223.5 ns/op 268.51 MB/s 87 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 78664455 14.76 ns/op 4063.92 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81305858 14.71 ns/op 4079.19 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81626406 14.71 ns/op 4078.32 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81168830 14.71 ns/op 4077.52 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81860040 14.72 ns/op 4075.37 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81093633 14.88 ns/op 4031.15 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 837949 1397 ns/op 62.29 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 869082 1380 ns/op 63.04 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 864015 1377 ns/op 63.18 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 873742 1374 ns/op 63.33 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 875703 1375 ns/op 63.27 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 866865 1375 ns/op 63.26 MB/s 194 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 772100 1709 ns/op 50.91 MB/s 543 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 2127564 1046 ns/op 83.14 MB/s 361 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6476034 1274 ns/op 68.30 MB/s 381 B/op 6 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6401709 221.3 ns/op 393.18 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6368766 220.2 ns/op 395.14 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6404850 220.6 ns/op 394.34 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 74606566 15.83 ns/op 5494.39 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76326774 15.72 ns/op 5536.01 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76140116 15.74 ns/op 5525.94 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76340330 15.69 ns/op 5544.89 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76240900 15.69 ns/op 5544.81 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76301294 15.73 ns/op 5531.49 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 753624 1592 ns/op 54.64 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 757292 1599 ns/op 54.42 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 758196 1588 ns/op 54.79 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 753902 1586 ns/op 54.85 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 758770 1589 ns/op 54.74 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 757748 1590 ns/op 54.71 MB/s 193 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 653979 1985 ns/op 43.82 MB/s 561 B/op 7 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 2344717 731.5 ns/op 118.93 MB/s 263 B/op 3 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6440574 1420 ns/op 61.26 MB/s 369 B/op 5 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6506366 238.2 ns/op 365.22 MB/s 107 B/op 2 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6504939 220.8 ns/op 394.05 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6399746 221.0 ns/op 393.66 MB/s 103 B/op 1 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75646941 15.95 ns/op 5453.57 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75406885 15.73 ns/op 5532.42 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76186243 15.69 ns/op 5545.76 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76350855 15.76 ns/op 5521.29 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76240896 15.70 ns/op 5542.36 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76404126 15.90 ns/op 5471.17 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 241440 4945 ns/op 109.19 MB/s 1181 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245013 5050 ns/op 106.94 MB/s 1180 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245098 4887 ns/op 110.49 MB/s 1177 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 244785 4971 ns/op 108.62 MB/s 1179 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245007 4880 ns/op 110.66 MB/s 1182 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245986 4878 ns/op 110.71 MB/s 1181 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 232534 5203 ns/op 103.78 MB/s 1845 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1000000 4309 ns/op 125.31 MB/s 1613 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3491629 4013 ns/op 134.57 MB/s 1471 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3670467 847.5 ns/op 637.15 MB/s 680 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3669694 385.0 ns/op 1402.66 MB/s 583 B/op 1 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3242532 356.5 ns/op 1514.63 MB/s 583 B/op 1 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 50391319 23.77 ns/op 22714.54 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51225590 23.32 ns/op 23159.25 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51732408 23.74 ns/op 22751.21 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 46074986 24.16 ns/op 22352.67 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 43649127 24.43 ns/op 22104.61 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 49954903 23.53 ns/op 22952.45 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 127574 9378 ns/op 57.58 MB/s 1180 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 128386 9386 ns/op 57.53 MB/s 1183 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 128604 9280 ns/op 58.19 MB/s 1178 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129218 9264 ns/op 58.29 MB/s 1179 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129030 9261 ns/op 58.31 MB/s 1179 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129080 9266 ns/op 58.28 MB/s 1180 B/op 3 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 123823 9282 ns/op 58.18 MB/s 1817 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1000000 8943 ns/op 60.38 MB/s 1754 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3532728 7337 ns/op 73.60 MB/s 1481 B/op 4 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3610767 705.9 ns/op 764.94 MB/s 626 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3502867 387.5 ns/op 1393.73 MB/s 583 B/op 1 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3706471 680.7 ns/op 793.25 MB/s 640 B/op 2 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51185895 24.01 ns/op 22492.97 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51442992 23.44 ns/op 23041.30 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 47312392 23.56 ns/op 22917.72 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51727110 23.33 ns/op 23144.01 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51212746 23.62 ns/op 22862.18 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51598200 23.23 ns/op 23247.62 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21105 57258 ns/op 35.80 MB/s 1389 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20656 57558 ns/op 35.62 MB/s 1386 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21045 57257 ns/op 35.80 MB/s 1386 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20884 57463 ns/op 35.68 MB/s 1391 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20984 56898 ns/op 36.03 MB/s 1388 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21164 57796 ns/op 35.47 MB/s 1388 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 103934 31906 ns/op 64.25 MB/s 3143 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 52097 ns/op 39.35 MB/s 3737 B/op 10 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1298925 14140 ns/op 144.98 MB/s 2637 B/op 4 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1288 ns/op 1592.17 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2546826 30224 ns/op 67.83 MB/s 3071 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 8376 ns/op 244.74 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 25786026 44.71 ns/op 45849.62 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27173578 44.15 ns/op 46427.72 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27221428 44.54 ns/op 46030.74 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27213686 44.07 ns/op 46519.79 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27233990 44.27 ns/op 46310.26 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27164018 44.12 ns/op 46460.92 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19785 60051 ns/op 34.14 MB/s 1386 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20198 60161 ns/op 34.08 MB/s 1391 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19585 60345 ns/op 33.97 MB/s 1390 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19956 61714 ns/op 33.22 MB/s 1391 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19554 61682 ns/op 33.24 MB/s 1388 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19830 60050 ns/op 34.14 MB/s 1393 B/op 9 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 38818 29507 ns/op 69.48 MB/s 3059 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 58539 ns/op 35.02 MB/s 3835 B/op 10 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2186653 33757 ns/op 60.73 MB/s 3157 B/op 6 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 1283 ns/op 1597.72 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1653430 1256 ns/op 1632.67 MB/s 2311 B/op 1 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2195628 2716 ns/op 754.79 MB/s 2317 B/op 2 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26531894 44.76 ns/op 45801.05 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26634384 44.68 ns/op 45878.57 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27184633 44.97 ns/op 45583.96 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27011893 44.46 ns/op 46104.62 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27183812 44.09 ns/op 46498.94 MB/s 0 B/op 0 allocs/op -BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27269318 44.17 ns/op 46406.38 MB/s 0 B/op 0 allocs/op -PASS -ok github.com/olekukonko/tablewriter/pkg/twwidth 724.296s -? github.com/olekukonko/tablewriter/renderer [no test files] -PASS -ok github.com/olekukonko/tablewriter/tests 2.959s -PASS -ok github.com/olekukonko/tablewriter/tw 0.270s diff --git a/vendor/github.com/olekukonko/tablewriter/option.go b/vendor/github.com/olekukonko/tablewriter/option.go index 0ec9844b0f..a895b193ea 100644 --- a/vendor/github.com/olekukonko/tablewriter/option.go +++ b/vendor/github.com/olekukonko/tablewriter/option.go @@ -528,6 +528,17 @@ func WithTrimSpace(state tw.State) Option { } } +// WithTrimTab sets whether leading and trailing tab characters are automatically trimmed. +// Logs the change if debugging is enabled. +func WithTrimTab(state tw.State) Option { + return func(target *Table) { + target.config.Behavior.TrimTab = state + if target.logger != nil { + target.logger.Debugf("Option: WithTrimTab applied to Table: %v", state) + } + } +} + // WithTrimLine sets whether empty visual lines within a cell are trimmed. // Logs the change if debugging is enabled. func WithTrimLine(state tw.State) Option { @@ -781,6 +792,7 @@ func defaultConfig() Config { Behavior: tw.Behavior{ AutoHide: tw.Off, TrimSpace: tw.On, + TrimTab: tw.On, TrimLine: tw.On, Structs: tw.Struct{ AutoHeader: tw.Off, @@ -920,6 +932,7 @@ func mergeConfig(dst, src Config) Config { dst.Debug = src.Debug || dst.Debug dst.Behavior.AutoHide = src.Behavior.AutoHide dst.Behavior.TrimSpace = src.Behavior.TrimSpace + dst.Behavior.TrimTab = src.Behavior.TrimTab dst.Behavior.Compact = src.Behavior.Compact dst.Behavior.Header = src.Behavior.Header dst.Behavior.Footer = src.Behavior.Footer diff --git a/vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go b/vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go index 5977aac26e..f6fa17e42d 100644 --- a/vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go +++ b/vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT // license that can be found in the LICENSE file. -// This module is a Table Writer API for the Go Programming Language. +// This module is a Table Writer API for the Go Programming Language. // The protocols were written in pure Go and works on windows and unix systems package twwarp @@ -13,8 +13,7 @@ import ( "unicode" "github.com/clipperhouse/uax29/v2/graphemes" - "github.com/olekukonko/tablewriter/pkg/twwidth" // IMPORT YOUR NEW PACKAGE - // "github.com/mattn/go-runewidth" // This can be removed if all direct uses are gone + "github.com/olekukonko/tablewriter/pkg/twwidth" ) const ( @@ -60,8 +59,7 @@ func WrapString(s string, lim int) ([]string, int) { var lines []string max := 0 for _, v := range words { - // max = runewidth.StringWidth(v) // OLD - max = twwidth.Width(v) // NEW: Use twdw.Width + max = twwidth.Width(v) if max > lim { lim = max } @@ -84,10 +82,8 @@ func WrapStringWithSpaces(s string, lim int) ([]string, int) { return []string{""}, lim } if strings.TrimSpace(s) == "" { // All spaces - // if runewidth.StringWidth(s) <= lim { // OLD - if twwidth.Width(s) <= lim { // NEW: Use twdw.Width - // return []string{s}, runewidth.StringWidth(s) // OLD - return []string{s}, twwidth.Width(s) // NEW: Use twdw.Width + if twwidth.Width(s) <= lim { + return []string{s}, twwidth.Width(s) } // For very long all-space strings, "wrap" by truncating to the limit. if lim > 0 { @@ -118,8 +114,7 @@ func WrapStringWithSpaces(s string, lim int) ([]string, int) { maxCoreWordWidth := 0 for _, v := range words { - // w := runewidth.StringWidth(v) // OLD - w := twwidth.Width(v) // NEW: Use twdw.Width + w := twwidth.Width(v) if w > maxCoreWordWidth { maxCoreWordWidth = w } @@ -156,8 +151,7 @@ func stringToDisplayWidth(s string, targetWidth int) (substring string, actualWi g := graphemes.FromString(s) for g.Next() { grapheme := g.Value() - // graphemeWidth := runewidth.StringWidth(grapheme) // OLD - graphemeWidth := twwidth.Width(grapheme) // NEW: Use twdw.Width + graphemeWidth := twwidth.Width(grapheme) if currentWidth+graphemeWidth > targetWidth { break @@ -187,8 +181,7 @@ func WrapWords(words []string, spc, lim, pen int) [][]string { } lengths := make([]int, n) for i := 0; i < n; i++ { - // lengths[i] = runewidth.StringWidth(words[i]) // OLD - lengths[i] = twwidth.Width(words[i]) // NEW: Use twdw.Width + lengths[i] = twwidth.Width(words[i]) } nbrk := make([]int, n) cost := make([]int, n) diff --git a/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/cache.go b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/cache.go new file mode 100644 index 0000000000..94e9746411 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/cache.go @@ -0,0 +1,26 @@ +package twwidth + +import "github.com/olekukonko/tablewriter/pkg/twcache" + +// widthCache stores memoized results of Width calculations to improve performance. +var widthCache *twcache.LRU[cacheKey, int] + +type cacheKey struct { + eastAsian bool + str string +} + +// SetCacheCapacity changes the cache size dynamically +// If capacity <= 0, disables caching entirely +func SetCacheCapacity(capacity int) { + mu.Lock() + defer mu.Unlock() + + if capacity <= 0 { + widthCache = nil // nil = fully disabled + return + } + + newCache := twcache.NewLRU[cacheKey, int](capacity) + widthCache = newCache +} diff --git a/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/tab.go b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/tab.go new file mode 100644 index 0000000000..c1f6c614f1 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/tab.go @@ -0,0 +1,288 @@ +package twwidth + +import ( + "bufio" + "os" + "path/filepath" + "strconv" + "strings" + "sync" +) + +type Tab rune + +const ( + TabWidthDefault = 8 + TabString Tab = '\t' +) + +// IsTab returns true if t equals the default tab. +func (t Tab) IsTab() bool { + return t == TabString +} + +func (t Tab) Byte() byte { + return byte(t) +} + +func (t Tab) Rune() rune { + return rune(t) +} + +func (t Tab) String() string { + return string(t) +} + +// IsTab returns true if r is a tab rune. +func IsTab(r rune) bool { + return r == TabString.Rune() +} + +type Tabinal struct { + once sync.Once + width int + mu sync.RWMutex +} + +func (t *Tabinal) String() string { + return TabString.String() +} + +// Size returns the current tab width, default if unset. +func (t *Tabinal) Size() int { + t.once.Do(t.init) + + t.mu.RLock() + w := t.width + t.mu.RUnlock() + + if w <= 0 { + return TabWidthDefault + } + return w +} + +// SetWidth sets the tab width if valid (1–32). +func (t *Tabinal) SetWidth(w int) { + if w <= 0 || w > 32 { + return + } + t.mu.Lock() + t.width = w + t.mu.Unlock() +} + +func (t *Tabinal) init() { + w := t.detect() + t.mu.Lock() + t.width = w + t.mu.Unlock() +} + +// detect determines tab width using env, editorconfig, project, or term. +func (t *Tabinal) detect() int { + if w := envInt("TABWIDTH"); w > 0 { + return clamp(w) + } + if w := envInt("TS"); w > 0 { + return clamp(w) + } + if w := envInt("VIM_TABSTOP"); w > 0 { + return clamp(w) + } + if w := editorConfigTabWidth(); w > 0 { + return w + } + if w := projectHeuristic(); w > 0 { + return w + } + if w := termHeuristic(); w > 0 { + return w + } + return 0 +} + +func editorConfigTabWidth() int { + dir, err := os.Getwd() + if err != nil { + return 0 + } + + for dir != "" && dir != "/" && dir != "." { + path := filepath.Join(dir, ".editorconfig") + if w := parseEditorConfig(path); w > 0 { + return clamp(w) + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + return 0 +} + +// parseEditorConfig reads tab_width or indent_size from a file. +func parseEditorConfig(path string) int { + f, err := os.Open(path) + if err != nil { + return 0 + } + defer f.Close() + + scanner := bufio.NewScanner(f) + inMatch := false + globalWidth := 0 + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") { + continue + } + + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + pattern := line[1 : len(line)-1] + inMatch = pattern == "*" + + knownExts := []string{".go", ".py", ".js", ".ts", ".java", ".rs"} + for _, ext := range knownExts { + if strings.Contains(pattern, ext) { + inMatch = true + break + } + } + continue + } + + if !inMatch && globalWidth == 0 { + continue + } + + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + continue + } + key := strings.TrimSpace(parts[0]) + val := strings.TrimSpace(parts[1]) + + switch key { + case "tab_width": + if w, err := strconv.Atoi(val); err == nil && w > 0 { + if inMatch { + return clamp(w) + } + if globalWidth == 0 { + globalWidth = w + } + } + case "indent_size": + if val == "tab" { + continue + } + if w, err := strconv.Atoi(val); err == nil && w > 0 { + if inMatch { + return clamp(w) + } + if globalWidth == 0 { + globalWidth = w + } + } + } + } + + return globalWidth +} + +// projectHeuristic returns 4 for known project types. +func projectHeuristic() int { + dir, err := os.Getwd() + if err != nil { + return 0 + } + + indicators := []string{ + "go.mod", "go.sum", + "package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml", + "setup.py", "pyproject.toml", "requirements.txt", "Pipfile", + "pom.xml", "build.gradle", "build.gradle.kts", + "Cargo.toml", + "composer.json", + } + + for _, indicator := range indicators { + if _, err := os.Stat(filepath.Join(dir, indicator)); err == nil { + return 4 + } + } + + patterns := []string{"*.go", "*.py", "*.js", "*.ts", "*.java", "*.rs"} + for _, pattern := range patterns { + if matches, _ := filepath.Glob(filepath.Join(dir, pattern)); len(matches) > 0 { + return 4 + } + } + + return 0 +} + +// termHeuristic returns a default width based on the TERM variable. +func termHeuristic() int { + termEnv := strings.ToLower(os.Getenv("TERM")) + if termEnv == "" { + return 0 + } + + if strings.Contains(termEnv, "vt52") { + return 2 + } + + if strings.Contains(termEnv, "xterm") || + strings.Contains(termEnv, "screen") || + strings.Contains(termEnv, "tmux") || + strings.Contains(termEnv, "linux") || + strings.Contains(termEnv, "ansi") || + strings.Contains(termEnv, "rxvt") { + return TabWidthDefault + } + + return 0 +} + +func clamp(w int) int { + if w <= 0 { + return 0 + } + if w > 32 { + return 32 + } + return w +} + +var ( + globalTab *Tabinal + globalTabOnce sync.Once +) + +// TabInstance returns the singleton Tabinal. +func TabInstance() *Tabinal { + globalTabOnce.Do(func() { + globalTab = &Tabinal{} + }) + return globalTab +} + +// TabWidth returns the detected global tab width. +func TabWidth() int { + return TabInstance().Size() +} + +// SetTabWidth sets the global tab width. +func SetTabWidth(w int) { + TabInstance().SetWidth(w) +} + +func envInt(k string) int { + v := os.Getenv(k) + w, _ := strconv.Atoi(v) + return w +} diff --git a/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go index 1a7582c860..14b334b095 100644 --- a/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go +++ b/vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go @@ -1,4 +1,3 @@ -// width.go package twwidth import ( @@ -34,9 +33,6 @@ var globalOptions Options // mu protects access to globalOptions for thread safety. var mu sync.Mutex -// widthCache stores memoized results of Width calculations to improve performance. -var widthCache *twcache.LRU[string, int] - // ansi is a compiled regular expression for stripping ANSI escape codes from strings. var ansi = Filter() @@ -53,24 +49,10 @@ func init() { // If EastAsianWidth is ON (e.g. forced via Env Var), but we detect // a modern environment, we might technically want to narrow borders // while keeping text wide. - // - // Note: In the standard EastAsian logic, isEastAsian will - // ALREADY be false for modern environments, so this boolean implies - // a specific "Forced On" scenario. ForceNarrowBorders: isEastAsian && isModernEnvironment(), } - widthCache = twcache.NewLRU[string, int](cacheCapacity) -} - -// makeCacheKey generates a string key for the LRU cache from the input string -// and the current East Asian width setting. -// Prefix "0:" for false, "1:" for true. -func makeCacheKey(str string, eastAsianWidth bool) string { - if eastAsianWidth { - return cacheEastAsianPrefix + str - } - return cachePrefix + str + widthCache = twcache.NewLRU[cacheKey, int](cacheCapacity) } // Display calculates the visual width of a string using a specific runewidth.Condition. @@ -124,21 +106,6 @@ func IsEastAsian() bool { return globalOptions.EastAsianWidth } -// SetCacheCapacity changes the cache size dynamically -// If capacity <= 0, disables caching entirely -func SetCacheCapacity(capacity int) { - mu.Lock() - defer mu.Unlock() - - if capacity <= 0 { - widthCache = nil // nil = fully disabled - return - } - - newCache := twcache.NewLRU[string, int](capacity) - widthCache = newCache -} - // SetCondition sets the global East Asian width setting based on a runewidth.Condition. // Deprecated: use SetOptions with the new twwidth.Options struct instead. // This function is kept for backward compatibility. @@ -362,6 +329,10 @@ func Truncate(s string, maxWidth int, suffix ...string) string { func Width(str string) int { // Fast path ASCII (Optimization) if len(str) == 1 && str[0] < 0x80 { + // Treat tab as special case even in fast path + if IsTab(rune(str[0])) { + return TabWidth() + } return 1 } @@ -369,17 +340,20 @@ func Width(str string) int { currentOpts := globalOptions mu.Unlock() - key := makeCacheKey(str, currentOpts.EastAsianWidth) + key := cacheKey{ + eastAsian: currentOpts.EastAsianWidth, + str: str, + } // Check Cache (Optimization) if w, found := widthCache.Get(key); found { return w } - stripped := ansi.ReplaceAllLiteralString(str, "") + //stripped := ansi.ReplaceAllLiteralString(str, "") calculatedWidth := 0 - for _, r := range stripped { + for _, r := range strip(str) { calculatedWidth += calculateRunewidth(r, currentOpts) } @@ -407,21 +381,27 @@ func WidthNoCache(str string) int { // bypassing the global settings and cache. This is useful for one-shot calculations // where global state is not desired. func WidthWithOptions(str string, opts Options) int { - stripped := ansi.ReplaceAllLiteralString(str, "") + // stripped := ansi.ReplaceAllLiteralString(str, "") calculatedWidth := 0 - for _, r := range stripped { + for _, r := range strip(str) { calculatedWidth += calculateRunewidth(r, opts) } return calculatedWidth } // calculateRunewidth calculates the width of a single rune based on the provided options. -// It applies narrow overrides for box drawing characters if configured. +// It applies narrow overrides for box drawing characters if configured and handles Tabs. func calculateRunewidth(r rune, opts Options) int { if opts.ForceNarrowBorders && isBoxDrawingChar(r) { return 1 } + // Explicitly handle Tabinal to ensure tables have enough space + // when TrimTab is Off. + if IsTab(r) { + return TabWidth() + } + dwOpts := displaywidth.Options{EastAsianWidth: opts.EastAsianWidth} return dwOpts.Rune(r) } @@ -430,3 +410,10 @@ func calculateRunewidth(r rune, opts Options) int { func isBoxDrawingChar(r rune) bool { return r >= 0x2500 && r <= 0x257F } + +func strip(s string) string { + if strings.IndexByte(s, '\x1b') == -1 { + return s + } + return ansi.ReplaceAllLiteralString(s, "") +} diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/blueprint.go b/vendor/github.com/olekukonko/tablewriter/renderer/blueprint.go index 48638fb23e..18bece5399 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/blueprint.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/blueprint.go @@ -44,7 +44,7 @@ func NewBlueprint(configs ...tw.Rendition) *Blueprint { // Merge user settings with default settings cfg.Settings = mergeSettings(cfg.Settings, userCfg.Settings) } - return &Blueprint{config: cfg, logger: ll.New("blueprint")} + return &Blueprint{config: cfg, logger: ll.New("blueprint").Disable()} } // Close performs cleanup (no-op in this implementation). @@ -322,14 +322,22 @@ func (f *Blueprint) formatCell(content string, width int, padding tw.Padding, al result.WriteString(content) rightPaddingWidth = totalPaddingWidth - padLeftWidth if rightPaddingWidth > 0 { - result.WriteString(tw.PadRight(tw.Empty, rightPadChar, rightPaddingWidth)) - f.logger.Debugf("Applied right padding: '%s' for %d width", rightPadChar, rightPaddingWidth) + padChar := rightPadChar + if padChar == tw.Empty { + padChar = tw.Space + } + result.WriteString(tw.PadRight(tw.Empty, padChar, rightPaddingWidth)) + f.logger.Debugf("Applied right padding: '%s' for %d width", padChar, rightPaddingWidth) } case tw.AlignRight: leftPaddingWidth = totalPaddingWidth - padRightWidth if leftPaddingWidth > 0 { - result.WriteString(tw.PadLeft(tw.Empty, leftPadChar, leftPaddingWidth)) - f.logger.Debugf("Applied left padding: '%s' for %d width", leftPadChar, leftPaddingWidth) + padChar := leftPadChar + if padChar == tw.Empty { + padChar = tw.Space + } + result.WriteString(tw.PadLeft(tw.Empty, padChar, leftPaddingWidth)) + f.logger.Debugf("Applied left padding: '%s' for %d width", padChar, leftPaddingWidth) } result.WriteString(content) result.WriteString(rightPadChar) @@ -337,15 +345,23 @@ func (f *Blueprint) formatCell(content string, width int, padding tw.Padding, al leftPaddingWidth = (totalPaddingWidth-padLeftWidth-padRightWidth)/2 + padLeftWidth rightPaddingWidth = totalPaddingWidth - leftPaddingWidth if leftPaddingWidth > padLeftWidth { - result.WriteString(tw.PadLeft(tw.Empty, leftPadChar, leftPaddingWidth-padLeftWidth)) - f.logger.Debugf("Applied left centering padding: '%s' for %d width", leftPadChar, leftPaddingWidth-padLeftWidth) + padChar := leftPadChar + if padChar == tw.Empty { + padChar = tw.Space + } + result.WriteString(tw.PadLeft(tw.Empty, padChar, leftPaddingWidth-padLeftWidth)) + f.logger.Debugf("Applied left centering padding: '%s' for %d width", padChar, leftPaddingWidth-padLeftWidth) } result.WriteString(leftPadChar) result.WriteString(content) result.WriteString(rightPadChar) if rightPaddingWidth > padRightWidth { - result.WriteString(tw.PadRight(tw.Empty, rightPadChar, rightPaddingWidth-padRightWidth)) - f.logger.Debugf("Applied right centering padding: '%s' for %d width", rightPadChar, rightPaddingWidth-padRightWidth) + padChar := rightPadChar + if padChar == tw.Empty { + padChar = tw.Space + } + result.WriteString(tw.PadRight(tw.Empty, padChar, rightPaddingWidth-padRightWidth)) + f.logger.Debugf("Applied right centering padding: '%s' for %d width", padChar, rightPaddingWidth-padRightWidth) } default: // Default to left alignment @@ -353,8 +369,12 @@ func (f *Blueprint) formatCell(content string, width int, padding tw.Padding, al result.WriteString(content) rightPaddingWidth = totalPaddingWidth - padLeftWidth if rightPaddingWidth > 0 { - result.WriteString(tw.PadRight(tw.Empty, rightPadChar, rightPaddingWidth)) - f.logger.Debugf("Applied right padding: '%s' for %d width", rightPadChar, rightPaddingWidth) + padChar := rightPadChar + if padChar == tw.Empty { + padChar = tw.Space + } + result.WriteString(tw.PadRight(tw.Empty, padChar, rightPaddingWidth)) + f.logger.Debugf("Applied right padding: '%s' for %d width", padChar, rightPaddingWidth) } } diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/colorized.go b/vendor/github.com/olekukonko/tablewriter/renderer/colorized.go index 1a0d87da14..21ef89463d 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/colorized.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/colorized.go @@ -103,7 +103,7 @@ func NewColorized(configs ...ColorizedConfig) *Colorized { tw.Row: tw.AlignLeft, tw.Footer: tw.AlignRight, }, - logger: ll.New("colorized", ll.WithHandler(lh.NewMemoryHandler())), + logger: ll.New("colorized", ll.WithHandler(lh.NewMemoryHandler())).Disable(), } // Log initialization details f.logger.Debugf("Initialized Colorized renderer with symbols: Center=%q, Row=%q, Column=%q", f.config.Symbols.Center(), f.config.Symbols.Row(), f.config.Symbols.Column()) @@ -354,19 +354,27 @@ func (c *Colorized) formatCell(content string, width int, padding tw.Padding, al // Calculate visual width of content contentVisualWidth := twwidth.Width(content) - // Set default padding characters + // Set padding characters padLeftCharStr := padding.Left - // if padLeftCharStr == tw.Empty { - // padLeftCharStr = tw.Space - //} padRightCharStr := padding.Right - // if padRightCharStr == tw.Empty { - // padRightCharStr = tw.Space - //} + + // Determine the character to use for alignment filling. + // We default to the padding character defined for that side. + // If the padding character is empty (e.g. Overwrite: true), we MUST fallback to Space + // for the alignment calculation to prevent the content from shifting incorrectly. + alignFillLeft := padLeftCharStr + if alignFillLeft == tw.Empty { + alignFillLeft = tw.Space + } + alignFillRight := padRightCharStr + if alignFillRight == tw.Empty { + alignFillRight = tw.Space + } // Calculate padding widths definedPadLeftWidth := twwidth.Width(padLeftCharStr) definedPadRightWidth := twwidth.Width(padRightCharStr) + // Calculate available width for content and alignment availableForContentAndAlign := max(width-definedPadLeftWidth-definedPadRightWidth, 0) @@ -381,21 +389,27 @@ func (c *Colorized) formatCell(content string, width int, padding tw.Padding, al remainingSpaceForAlignment := max(availableForContentAndAlign-contentVisualWidth, 0) // Apply alignment padding + // Note: We use tw.Pad* helpers here instead of strings.Repeat to handle multi-byte fill chars correctly. leftAlignmentPadSpaces := tw.Empty rightAlignmentPadSpaces := tw.Empty + switch align { case tw.AlignLeft: - rightAlignmentPadSpaces = strings.Repeat(tw.Space, remainingSpaceForAlignment) + rightAlignmentPadSpaces = tw.PadRight(tw.Empty, alignFillRight, remainingSpaceForAlignment) case tw.AlignRight: - leftAlignmentPadSpaces = strings.Repeat(tw.Space, remainingSpaceForAlignment) + leftAlignmentPadSpaces = tw.PadLeft(tw.Empty, alignFillLeft, remainingSpaceForAlignment) case tw.AlignCenter: leftSpacesCount := remainingSpaceForAlignment / 2 rightSpacesCount := remainingSpaceForAlignment - leftSpacesCount - leftAlignmentPadSpaces = strings.Repeat(tw.Space, leftSpacesCount) - rightAlignmentPadSpaces = strings.Repeat(tw.Space, rightSpacesCount) + if leftSpacesCount > 0 { + leftAlignmentPadSpaces = tw.PadLeft(tw.Empty, alignFillLeft, leftSpacesCount) + } + if rightSpacesCount > 0 { + rightAlignmentPadSpaces = tw.PadRight(tw.Empty, alignFillRight, rightSpacesCount) + } default: // Default to left alignment - rightAlignmentPadSpaces = strings.Repeat(tw.Space, remainingSpaceForAlignment) + rightAlignmentPadSpaces = tw.PadRight(tw.Empty, alignFillRight, remainingSpaceForAlignment) } // Apply colors to content and padding @@ -444,7 +458,7 @@ func (c *Colorized) formatCell(content string, width int, padding tw.Padding, al sb.WriteString(coloredPadRight) output := sb.String() - // Adjust output width if necessary + // Adjust output width if necessary (safety check) currentVisualWidth := twwidth.Width(output) if currentVisualWidth != width { c.logger.Debugf("formatCell MISMATCH: content='%s', target_w=%d. Calculated parts width = %d. String: '%s'", diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/html.go b/vendor/github.com/olekukonko/tablewriter/renderer/html.go index d02594b9ce..c6fa2c919f 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/html.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/html.go @@ -64,7 +64,7 @@ func NewHTML(configs ...HTMLConfig) *HTML { tableStarted: false, tbodyStarted: false, tfootStarted: false, - logger: ll.New("html"), + logger: ll.New("html").Disable(), } } diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go b/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go index 936889de37..c8ff55a9d0 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/markdown.go @@ -37,7 +37,7 @@ func NewMarkdown(configs ...tw.Rendition) *Markdown { if len(configs) > 0 { cfg = mergeMarkdownConfig(cfg, configs[0]) } - return &Markdown{config: cfg, logger: ll.New("markdown")} + return &Markdown{config: cfg, logger: ll.New("markdown").Disable()} } // mergeMarkdownConfig combines user-provided config with Markdown defaults, enforcing Markdown-specific settings. diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go b/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go index 230220d26b..c6ff2b9bc1 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/ocean.go @@ -36,7 +36,7 @@ func NewOcean(oceanConfig ...OceanConfig) *Ocean { config: cfg, oceanConfig: oCfg, fixedWidths: tw.NewMapper[int, int](), - logger: ll.New("ocean"), + logger: ll.New("ocean").Disable(), } r.resetState() return r diff --git a/vendor/github.com/olekukonko/tablewriter/renderer/svg.go b/vendor/github.com/olekukonko/tablewriter/renderer/svg.go index b725754cf1..c7d7f1c180 100644 --- a/vendor/github.com/olekukonko/tablewriter/renderer/svg.go +++ b/vendor/github.com/olekukonko/tablewriter/renderer/svg.go @@ -139,7 +139,7 @@ func NewSVG(configs ...SVGConfig) *SVG { allVisualLineData: make([][][]string, 3), allVisualLineCtx: make([][]tw.Formatting, 3), vMergeTrack: make(map[int]int), - logger: ll.New("svg"), + logger: ll.New("svg").Disable(), } for i := 0; i < 3; i++ { r.allVisualLineData[i] = make([][]string, 0) diff --git a/vendor/github.com/olekukonko/tablewriter/tablewriter.go b/vendor/github.com/olekukonko/tablewriter/tablewriter.go index 1cea0b94ba..9fd7404565 100644 --- a/vendor/github.com/olekukonko/tablewriter/tablewriter.go +++ b/vendor/github.com/olekukonko/tablewriter/tablewriter.go @@ -7,6 +7,7 @@ import ( "reflect" "runtime" "strings" + "unicode" "github.com/olekukonko/errors" "github.com/olekukonko/ll" @@ -412,6 +413,15 @@ func (t *Table) Options(opts ...Option) *Table { t.logger = ll.New("table").Handler(lh.NewTextHandler(t.trace)) } + // Disable and suspend the logger before applying options to prevent premature + // debug output from renderer methods (e.g., Blueprint.Rendition) triggered by + // options like WithRendition. Without this, a previously-enabled logger would + // still be active on the renderer during option application, causing debug + // messages even when WithDebug(false) is being applied. + t.logger.Disable() + t.logger.Suspend() + t.renderer.Logger(t.logger) + // loop through options for _, opt := range opts { opt(t) @@ -546,16 +556,37 @@ func (t *Table) Counters() []tw.Counter { } // Trimmer trims whitespace from a string based on the Table’s configuration. -// It conditionally applies strings.TrimSpace to the input string if the TrimSpace behavior -// is enabled in t.config.Behavior, otherwise returning the string unchanged. This method -// is used in the logging library to format strings for tabular output, ensuring consistent -// display in log messages. Thread-safe as it only reads configuration and operates on the -// input string. +// It conditionally applies trimming based on TrimSpace and TrimTab settings. +// +// Behavior Matrix: +// - TrimSpace=On, TrimTab=On: Uses strings.TrimSpace (removes all Unicode space including \t). +// - TrimSpace=On, TrimTab=Off: Removes spaces/newlines but PRESERVES tabs. +// - TrimSpace=Off, TrimTab=On: Removes only tabs. +// - TrimSpace=Off, TrimTab=Off: Returns string unchanged. func (t *Table) Trimmer(str string) string { - if t.config.Behavior.TrimSpace.Enabled() { + trimSpace := t.config.Behavior.TrimSpace.Enabled() + trimTab := t.config.Behavior.TrimTab.Enabled() + + // Fast Path 1: If both are enabled (Default), use the stdlib optimized TrimSpace + if trimSpace && trimTab { return strings.TrimSpace(str) } - return str + + // Fast Path 2: If both are disabled, return raw string + if !trimSpace && !trimTab { + return str + } + + // Granular Trimming via TrimFunc + return strings.TrimFunc(str, func(r rune) bool { + if twwidth.IsTab(r) { + return trimTab // Return true to trim if TrimTab is On + } + if trimSpace { + return unicode.IsSpace(r) // Trim other whitespace if TrimSpace is On + } + return false + }) } // appendSingle adds a single row to the table's row data. @@ -935,6 +966,13 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string cellContent = t.Trimmer(cellContent) + if strings.Contains(cellContent, twwidth.TabString.String()) { + // Get the detected width from the singleton + width := twwidth.TabWidth() + spaces := strings.Repeat(tw.Space, width) + cellContent = strings.ReplaceAll(cellContent, twwidth.TabString.String(), spaces) + } + colPad := config.Padding.Global if i < len(config.Padding.PerColumn) && config.Padding.PerColumn[i].Paddable() { colPad = config.Padding.PerColumn[i] @@ -956,7 +994,7 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string switch config.Formatting.AutoWrap { case tw.WrapNormal: var wrapped []string - if t.config.Behavior.TrimSpace.Enabled() { + if t.config.Behavior.TrimSpace.Enabled() && t.config.Behavior.TrimTab.Enabled() { wrapped, _ = twwarp.WrapString(line, effectiveContentMaxWidth) } else { wrapped, _ = twwarp.WrapStringWithSpaces(line, effectiveContentMaxWidth) diff --git a/vendor/github.com/olekukonko/tablewriter/tw/types.go b/vendor/github.com/olekukonko/tablewriter/tw/types.go index 54a9b86ef8..bc84756a35 100644 --- a/vendor/github.com/olekukonko/tablewriter/tw/types.go +++ b/vendor/github.com/olekukonko/tablewriter/tw/types.go @@ -166,8 +166,9 @@ type Struct struct { // Behavior defines settings that control table rendering behaviors, such as column visibility and content formatting. type Behavior struct { AutoHide State // AutoHide determines whether empty columns are hidden. Ignored in streaming mode. - TrimSpace State // TrimSpace enables trimming of leading and trailing spaces from cell content. + TrimSpace State // TrimSpace determines trimming of leading and trailing spaces from cell content. TrimLine State // TrimLine determines whether empty visual lines within a cell are collapsed. + TrimTab State // TrimTab determines trimming of leading and trailing tabs from cell content. Header Control // Header specifies control settings for the table header. Footer Control // Footer specifies control settings for the table footer. diff --git a/vendor/modules.txt b/vendor/modules.txt index b18c8c5115..7aff77ae47 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -255,16 +255,12 @@ github.com/cespare/xxhash/v2 # github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 ## explicit github.com/cevaris/ordered_map -# github.com/clipperhouse/displaywidth v0.6.2 +# github.com/clipperhouse/displaywidth v0.10.0 ## explicit; go 1.18 github.com/clipperhouse/displaywidth -# github.com/clipperhouse/stringish v0.1.1 -## explicit; go 1.18 -github.com/clipperhouse/stringish -# github.com/clipperhouse/uax29/v2 v2.3.0 +# github.com/clipperhouse/uax29/v2 v2.6.0 ## explicit; go 1.18 github.com/clipperhouse/uax29/v2/graphemes -github.com/clipperhouse/uax29/v2/internal/iterators # github.com/cloudflare/circl v1.6.3 ## explicit; go 1.22.0 github.com/cloudflare/circl/dh/x25519 @@ -1209,15 +1205,15 @@ github.com/oklog/run # github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 ## explicit; go 1.21 github.com/olekukonko/cat -# github.com/olekukonko/errors v1.1.0 +# github.com/olekukonko/errors v1.2.0 ## explicit; go 1.21 github.com/olekukonko/errors -# github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 +# github.com/olekukonko/ll v0.1.6 ## explicit; go 1.21 github.com/olekukonko/ll github.com/olekukonko/ll/lh github.com/olekukonko/ll/lx -# github.com/olekukonko/tablewriter v1.1.3 +# github.com/olekukonko/tablewriter v1.1.4 ## explicit; go 1.21 github.com/olekukonko/tablewriter github.com/olekukonko/tablewriter/pkg/twcache