diff --git a/go.mod b/go.mod index 892d0f7..58826c0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/etkecc/go-secgen v1.4.0 github.com/etkecc/go-validator/v2 v2.2.6 github.com/fsnotify/fsnotify v1.9.0 - github.com/gabriel-vasile/mimetype v1.4.11 + github.com/gabriel-vasile/mimetype v1.4.12 github.com/getsentry/sentry-go v0.40.0 github.com/jhillyerd/enmime/v2 v2.2.0 github.com/kvannotten/mailstrip v0.0.0-20200711213611-0002f5c0467e @@ -26,9 +26,9 @@ require ( github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39 github.com/rs/zerolog v1.34.0 github.com/swaggo/swag v1.16.3 - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 - maunium.net/go/mautrix v0.26.0 - modernc.org/sqlite v1.40.1 + golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 + maunium.net/go/mautrix v0.26.1 + modernc.org/sqlite v1.41.0 ) require ( @@ -39,7 +39,7 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect - github.com/clipperhouse/displaywidth v0.6.1 // 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/dustin/go-humanize v1.0.1 // indirect @@ -74,12 +74,12 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/yuin/goldmark v1.7.13 // indirect - go.mau.fi/util v0.9.3 // indirect + go.mau.fi/util v0.9.4 // indirect golang.org/x/crypto v0.46.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect - golang.org/x/tools v0.39.0 // indirect + golang.org/x/tools v0.40.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect modernc.org/libc v1.67.1 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index 8d68c1c..d288f7d 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= -github.com/clipperhouse/displaywidth v0.6.1 h1:/zMlAezfDzT2xy6acHBzwIfyu2ic0hgkT83UX5EY2gY= -github.com/clipperhouse/displaywidth v0.6.1/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= +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= @@ -59,8 +59,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= -github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo= github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -175,16 +175,16 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU= -go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ= +go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso= +go.mau.fi/util v0.9.4/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= @@ -206,8 +206,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -220,8 +220,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -maunium.net/go/mautrix v0.26.0 h1:valc2VmZF+oIY4bMq4Cd5H9cEKMRe8eP4FM7iiaYLxI= -maunium.net/go/mautrix v0.26.0/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= +maunium.net/go/mautrix v0.26.1 h1:FWCC1xY5vwJ5ou3duEBjB6w9IIlwfc9el3q3Mju3Dlg= +maunium.net/go/mautrix v0.26.1/go.mod h1:UySSpb8OqXG1sMJ6dDqyzmfcqr2ayZK+KzwqOTAkAOM= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= @@ -244,8 +244,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY= -modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= +modernc.org/sqlite v1.41.0 h1:bJXddp4ZpsqMsNN1vS0jWo4IJTZzb8nWpcgvyCFG9Ck= +modernc.org/sqlite v1.41.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/bot/sync.go b/internal/bot/sync.go index bfbbb1e..929aed1 100644 --- a/internal/bot/sync.go +++ b/internal/bot/sync.go @@ -91,6 +91,13 @@ func (b *Bot) onReaction(ctx context.Context, evt *event.Event) { // onBotJoin handles the "bot joined the room" event func (b *Bot) onBotJoin(ctx context.Context) { evt := eventFromContext(ctx) + + // do not send introduction/help messages to mailbox rooms where just a profile change occurred + cfg, err := b.cfg.GetRoom(ctx, evt.RoomID) + if err == nil && cfg.Mailbox() != "" { + return + } + // Workaround for membership=join events which are delivered to us twice, // as described in this bug report: https://github.com/matrix-org/synapse/issues/9768 _, ok := b.handledMembershipEvents.LoadOrStore(evt.ID, true) diff --git a/vendor/github.com/clipperhouse/displaywidth/trie.go b/vendor/github.com/clipperhouse/displaywidth/trie.go index c63ed32..b03f882 100644 --- a/vendor/github.com/clipperhouse/displaywidth/trie.go +++ b/vendor/github.com/clipperhouse/displaywidth/trie.go @@ -10,14 +10,10 @@ type property uint8 const ( // Always 0 width, includes combining marks, control characters, non-printable, etc _Zero_Width property = iota + 1 - // Always 2 wide (East Asian Wide F/W) - _East_Asian_Wide + // Always 2 wide (East Asian Wide F/W, Emoji, Regional Indicator) + _Wide // Width depends on EastAsianWidth option _East_Asian_Ambiguous - // Extended_Pictographic + Emoji_Presentation - _Emoji - // Regional Indicator symbols (used in flag emoji pairs) - _Regional_Indicator ) // lookup returns the trie value for the first UTF-8 encoding in s and @@ -83,7 +79,7 @@ func lookup[T stringish.Interface](s T) (v uint8, sz int) { return 0, 1 } -// stringWidthTrie. Total size: 17792 bytes (17.38 KiB). Checksum: a03a02693a639d4a. +// stringWidthTrie. Total size: 17664 bytes (17.25 KiB). Checksum: c77d82ff2d69f0d2. // type stringWidthTrie struct { } // func newStringWidthTrie(i int) *stringWidthTrie { @@ -98,9 +94,9 @@ func lookupValue(n uint32, b byte) uint8 { } } -// stringWidthValues: 248 blocks, 15872 entries, 15872 bytes +// stringWidthValues: 246 blocks, 15744 entries, 15744 bytes // The third block is the zero block. -var stringWidthValues = [15872]uint8{ +var stringWidthValues = [15744]uint8{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 @@ -579,13 +575,13 @@ var stringWidthValues = [15872]uint8{ 0x167f: 0x0003, // Block 0x5a, offset 0x1680 0x1692: 0x0003, - 0x169a: 0x0004, 0x169b: 0x0004, + 0x169a: 0x0002, 0x169b: 0x0002, 0x16a9: 0x0002, 0x16aa: 0x0002, // Block 0x5b, offset 0x16c0 - 0x16e9: 0x0004, - 0x16ea: 0x0004, 0x16eb: 0x0004, 0x16ec: 0x0004, - 0x16f0: 0x0004, 0x16f3: 0x0004, + 0x16e9: 0x0002, + 0x16ea: 0x0002, 0x16eb: 0x0002, 0x16ec: 0x0002, + 0x16f0: 0x0002, 0x16f3: 0x0002, // Block 0x5c, offset 0x1700 0x1720: 0x0003, 0x1721: 0x0003, 0x1722: 0x0003, 0x1723: 0x0003, 0x1724: 0x0003, 0x1725: 0x0003, 0x1726: 0x0003, 0x1727: 0x0003, 0x1728: 0x0003, 0x1729: 0x0003, @@ -644,63 +640,63 @@ var stringWidthValues = [15872]uint8{ 0x1862: 0x0003, 0x1863: 0x0003, 0x1864: 0x0003, 0x1865: 0x0003, 0x186f: 0x0003, - 0x187d: 0x0004, 0x187e: 0x0004, + 0x187d: 0x0002, 0x187e: 0x0002, // Block 0x62, offset 0x1880 0x1885: 0x0003, 0x1886: 0x0003, 0x1889: 0x0003, 0x188e: 0x0003, 0x188f: 0x0003, - 0x1894: 0x0004, 0x1895: 0x0004, + 0x1894: 0x0002, 0x1895: 0x0002, 0x189c: 0x0003, 0x189e: 0x0003, 0x18b0: 0x0002, 0x18b1: 0x0002, 0x18b2: 0x0002, 0x18b3: 0x0002, 0x18b4: 0x0002, 0x18b5: 0x0002, 0x18b6: 0x0002, 0x18b7: 0x0002, // Block 0x63, offset 0x18c0 0x18c0: 0x0003, 0x18c2: 0x0003, - 0x18c8: 0x0004, 0x18c9: 0x0004, 0x18ca: 0x0004, 0x18cb: 0x0004, - 0x18cc: 0x0004, 0x18cd: 0x0004, 0x18ce: 0x0004, 0x18cf: 0x0004, 0x18d0: 0x0004, 0x18d1: 0x0004, - 0x18d2: 0x0004, 0x18d3: 0x0004, + 0x18c8: 0x0002, 0x18c9: 0x0002, 0x18ca: 0x0002, 0x18cb: 0x0002, + 0x18cc: 0x0002, 0x18cd: 0x0002, 0x18ce: 0x0002, 0x18cf: 0x0002, 0x18d0: 0x0002, 0x18d1: 0x0002, + 0x18d2: 0x0002, 0x18d3: 0x0002, 0x18e0: 0x0003, 0x18e1: 0x0003, 0x18e3: 0x0003, 0x18e4: 0x0003, 0x18e5: 0x0003, 0x18e7: 0x0003, 0x18e8: 0x0003, 0x18e9: 0x0003, 0x18ea: 0x0003, 0x18ec: 0x0003, 0x18ed: 0x0003, 0x18ef: 0x0003, - 0x18ff: 0x0004, + 0x18ff: 0x0002, // Block 0x64, offset 0x1900 0x190a: 0x0002, 0x190b: 0x0002, 0x190c: 0x0002, 0x190d: 0x0002, 0x190e: 0x0002, 0x190f: 0x0002, - 0x1913: 0x0004, - 0x191e: 0x0003, 0x191f: 0x0003, 0x1921: 0x0004, - 0x192a: 0x0004, 0x192b: 0x0004, - 0x193d: 0x0004, 0x193e: 0x0004, 0x193f: 0x0003, + 0x1913: 0x0002, + 0x191e: 0x0003, 0x191f: 0x0003, 0x1921: 0x0002, + 0x192a: 0x0002, 0x192b: 0x0002, + 0x193d: 0x0002, 0x193e: 0x0002, 0x193f: 0x0003, // Block 0x65, offset 0x1940 - 0x1944: 0x0004, 0x1945: 0x0004, + 0x1944: 0x0002, 0x1945: 0x0002, 0x1946: 0x0003, 0x1947: 0x0003, 0x1948: 0x0003, 0x1949: 0x0003, 0x194a: 0x0003, 0x194b: 0x0003, - 0x194c: 0x0003, 0x194d: 0x0003, 0x194e: 0x0004, 0x194f: 0x0003, 0x1950: 0x0003, 0x1951: 0x0003, - 0x1952: 0x0003, 0x1953: 0x0003, 0x1954: 0x0004, 0x1955: 0x0003, 0x1956: 0x0003, 0x1957: 0x0003, + 0x194c: 0x0003, 0x194d: 0x0003, 0x194e: 0x0002, 0x194f: 0x0003, 0x1950: 0x0003, 0x1951: 0x0003, + 0x1952: 0x0003, 0x1953: 0x0003, 0x1954: 0x0002, 0x1955: 0x0003, 0x1956: 0x0003, 0x1957: 0x0003, 0x1958: 0x0003, 0x1959: 0x0003, 0x195a: 0x0003, 0x195b: 0x0003, 0x195c: 0x0003, 0x195d: 0x0003, 0x195e: 0x0003, 0x195f: 0x0003, 0x1960: 0x0003, 0x1961: 0x0003, 0x1963: 0x0003, 0x1968: 0x0003, 0x1969: 0x0003, - 0x196a: 0x0004, 0x196b: 0x0003, 0x196c: 0x0003, 0x196d: 0x0003, 0x196e: 0x0003, 0x196f: 0x0003, - 0x1970: 0x0003, 0x1971: 0x0003, 0x1972: 0x0004, 0x1973: 0x0004, 0x1974: 0x0003, 0x1975: 0x0004, - 0x1976: 0x0003, 0x1977: 0x0003, 0x1978: 0x0003, 0x1979: 0x0003, 0x197a: 0x0004, 0x197b: 0x0003, - 0x197c: 0x0003, 0x197d: 0x0004, 0x197e: 0x0003, 0x197f: 0x0003, + 0x196a: 0x0002, 0x196b: 0x0003, 0x196c: 0x0003, 0x196d: 0x0003, 0x196e: 0x0003, 0x196f: 0x0003, + 0x1970: 0x0003, 0x1971: 0x0003, 0x1972: 0x0002, 0x1973: 0x0002, 0x1974: 0x0003, 0x1975: 0x0002, + 0x1976: 0x0003, 0x1977: 0x0003, 0x1978: 0x0003, 0x1979: 0x0003, 0x197a: 0x0002, 0x197b: 0x0003, + 0x197c: 0x0003, 0x197d: 0x0002, 0x197e: 0x0003, 0x197f: 0x0003, // Block 0x66, offset 0x1980 - 0x1985: 0x0004, - 0x198a: 0x0004, 0x198b: 0x0004, - 0x19a8: 0x0004, + 0x1985: 0x0002, + 0x198a: 0x0002, 0x198b: 0x0002, + 0x19a8: 0x0002, 0x19bd: 0x0003, // Block 0x67, offset 0x19c0 - 0x19cc: 0x0004, 0x19ce: 0x0004, - 0x19d3: 0x0004, 0x19d4: 0x0004, 0x19d5: 0x0004, 0x19d7: 0x0004, + 0x19cc: 0x0002, 0x19ce: 0x0002, + 0x19d3: 0x0002, 0x19d4: 0x0002, 0x19d5: 0x0002, 0x19d7: 0x0002, 0x19f6: 0x0003, 0x19f7: 0x0003, 0x19f8: 0x0003, 0x19f9: 0x0003, 0x19fa: 0x0003, 0x19fb: 0x0003, 0x19fc: 0x0003, 0x19fd: 0x0003, 0x19fe: 0x0003, 0x19ff: 0x0003, // Block 0x68, offset 0x1a00 - 0x1a15: 0x0004, 0x1a16: 0x0004, 0x1a17: 0x0004, - 0x1a30: 0x0004, - 0x1a3f: 0x0004, + 0x1a15: 0x0002, 0x1a16: 0x0002, 0x1a17: 0x0002, + 0x1a30: 0x0002, + 0x1a3f: 0x0002, // Block 0x69, offset 0x1a40 - 0x1a5b: 0x0004, 0x1a5c: 0x0004, + 0x1a5b: 0x0002, 0x1a5c: 0x0002, // Block 0x6a, offset 0x1a80 - 0x1a90: 0x0004, - 0x1a95: 0x0004, 0x1a96: 0x0003, 0x1a97: 0x0003, + 0x1a90: 0x0002, + 0x1a95: 0x0002, 0x1a96: 0x0003, 0x1a97: 0x0003, 0x1a98: 0x0003, 0x1a99: 0x0003, // Block 0x6b, offset 0x1ac0 0x1aef: 0x0001, @@ -1275,9 +1271,9 @@ var stringWidthValues = [15872]uint8{ 0x3604: 0x0001, 0x3605: 0x0001, 0x3606: 0x0001, 0x3607: 0x0001, 0x3608: 0x0001, 0x3609: 0x0001, 0x360a: 0x0001, // Block 0xd9, offset 0x3640 - 0x3644: 0x0004, + 0x3644: 0x0002, // Block 0xda, offset 0x3680 - 0x368f: 0x0004, + 0x368f: 0x0002, // Block 0xdb, offset 0x36c0 0x36c0: 0x0003, 0x36c1: 0x0003, 0x36c2: 0x0003, 0x36c3: 0x0003, 0x36c4: 0x0003, 0x36c5: 0x0003, 0x36c6: 0x0003, 0x36c7: 0x0003, 0x36c8: 0x0003, 0x36c9: 0x0003, 0x36ca: 0x0003, @@ -1304,252 +1300,228 @@ var stringWidthValues = [15872]uint8{ // Block 0xdd, offset 0x3740 0x3740: 0x0003, 0x3741: 0x0003, 0x3742: 0x0003, 0x3743: 0x0003, 0x3744: 0x0003, 0x3745: 0x0003, 0x3746: 0x0003, 0x3747: 0x0003, 0x3748: 0x0003, 0x3749: 0x0003, 0x374a: 0x0003, 0x374b: 0x0003, - 0x374c: 0x0003, 0x374d: 0x0003, 0x374e: 0x0004, 0x374f: 0x0003, 0x3750: 0x0003, 0x3751: 0x0004, - 0x3752: 0x0004, 0x3753: 0x0004, 0x3754: 0x0004, 0x3755: 0x0004, 0x3756: 0x0004, 0x3757: 0x0004, - 0x3758: 0x0004, 0x3759: 0x0004, 0x375a: 0x0004, 0x375b: 0x0003, 0x375c: 0x0003, 0x375d: 0x0003, + 0x374c: 0x0003, 0x374d: 0x0003, 0x374e: 0x0002, 0x374f: 0x0003, 0x3750: 0x0003, 0x3751: 0x0002, + 0x3752: 0x0002, 0x3753: 0x0002, 0x3754: 0x0002, 0x3755: 0x0002, 0x3756: 0x0002, 0x3757: 0x0002, + 0x3758: 0x0002, 0x3759: 0x0002, 0x375a: 0x0002, 0x375b: 0x0003, 0x375c: 0x0003, 0x375d: 0x0003, 0x375e: 0x0003, 0x375f: 0x0003, 0x3760: 0x0003, 0x3761: 0x0003, 0x3762: 0x0003, 0x3763: 0x0003, 0x3764: 0x0003, 0x3765: 0x0003, 0x3766: 0x0003, 0x3767: 0x0003, 0x3768: 0x0003, 0x3769: 0x0003, 0x376a: 0x0003, 0x376b: 0x0003, 0x376c: 0x0003, // Block 0xde, offset 0x3780 - 0x37a6: 0x0005, 0x37a7: 0x0005, 0x37a8: 0x0005, 0x37a9: 0x0005, - 0x37aa: 0x0005, 0x37ab: 0x0005, 0x37ac: 0x0005, 0x37ad: 0x0005, 0x37ae: 0x0005, 0x37af: 0x0005, - 0x37b0: 0x0005, 0x37b1: 0x0005, 0x37b2: 0x0005, 0x37b3: 0x0005, 0x37b4: 0x0005, 0x37b5: 0x0005, - 0x37b6: 0x0005, 0x37b7: 0x0005, 0x37b8: 0x0005, 0x37b9: 0x0005, 0x37ba: 0x0005, 0x37bb: 0x0005, - 0x37bc: 0x0005, 0x37bd: 0x0005, 0x37be: 0x0005, 0x37bf: 0x0005, + 0x37a6: 0x0002, 0x37a7: 0x0002, 0x37a8: 0x0002, 0x37a9: 0x0002, + 0x37aa: 0x0002, 0x37ab: 0x0002, 0x37ac: 0x0002, 0x37ad: 0x0002, 0x37ae: 0x0002, 0x37af: 0x0002, + 0x37b0: 0x0002, 0x37b1: 0x0002, 0x37b2: 0x0002, 0x37b3: 0x0002, 0x37b4: 0x0002, 0x37b5: 0x0002, + 0x37b6: 0x0002, 0x37b7: 0x0002, 0x37b8: 0x0002, 0x37b9: 0x0002, 0x37ba: 0x0002, 0x37bb: 0x0002, + 0x37bc: 0x0002, 0x37bd: 0x0002, 0x37be: 0x0002, 0x37bf: 0x0002, // Block 0xdf, offset 0x37c0 - 0x37c0: 0x0002, 0x37c1: 0x0004, 0x37c2: 0x0002, + 0x37c0: 0x0002, 0x37c1: 0x0002, 0x37c2: 0x0002, 0x37d0: 0x0002, 0x37d1: 0x0002, 0x37d2: 0x0002, 0x37d3: 0x0002, 0x37d4: 0x0002, 0x37d5: 0x0002, 0x37d6: 0x0002, 0x37d7: 0x0002, - 0x37d8: 0x0002, 0x37d9: 0x0002, 0x37da: 0x0004, 0x37db: 0x0002, 0x37dc: 0x0002, 0x37dd: 0x0002, + 0x37d8: 0x0002, 0x37d9: 0x0002, 0x37da: 0x0002, 0x37db: 0x0002, 0x37dc: 0x0002, 0x37dd: 0x0002, 0x37de: 0x0002, 0x37df: 0x0002, 0x37e0: 0x0002, 0x37e1: 0x0002, 0x37e2: 0x0002, 0x37e3: 0x0002, 0x37e4: 0x0002, 0x37e5: 0x0002, 0x37e6: 0x0002, 0x37e7: 0x0002, 0x37e8: 0x0002, 0x37e9: 0x0002, - 0x37ea: 0x0002, 0x37eb: 0x0002, 0x37ec: 0x0002, 0x37ed: 0x0002, 0x37ee: 0x0002, 0x37ef: 0x0004, - 0x37f0: 0x0002, 0x37f1: 0x0002, 0x37f2: 0x0004, 0x37f3: 0x0004, 0x37f4: 0x0004, 0x37f5: 0x0004, - 0x37f6: 0x0004, 0x37f7: 0x0002, 0x37f8: 0x0004, 0x37f9: 0x0004, 0x37fa: 0x0004, 0x37fb: 0x0002, + 0x37ea: 0x0002, 0x37eb: 0x0002, 0x37ec: 0x0002, 0x37ed: 0x0002, 0x37ee: 0x0002, 0x37ef: 0x0002, + 0x37f0: 0x0002, 0x37f1: 0x0002, 0x37f2: 0x0002, 0x37f3: 0x0002, 0x37f4: 0x0002, 0x37f5: 0x0002, + 0x37f6: 0x0002, 0x37f7: 0x0002, 0x37f8: 0x0002, 0x37f9: 0x0002, 0x37fa: 0x0002, 0x37fb: 0x0002, // Block 0xe0, offset 0x3800 0x3800: 0x0002, 0x3801: 0x0002, 0x3802: 0x0002, 0x3803: 0x0002, 0x3804: 0x0002, 0x3805: 0x0002, 0x3806: 0x0002, 0x3807: 0x0002, 0x3808: 0x0002, - 0x3810: 0x0004, 0x3811: 0x0004, + 0x3810: 0x0002, 0x3811: 0x0002, 0x3820: 0x0002, 0x3821: 0x0002, 0x3822: 0x0002, 0x3823: 0x0002, 0x3824: 0x0002, 0x3825: 0x0002, // Block 0xe1, offset 0x3840 - 0x3840: 0x0004, 0x3841: 0x0004, 0x3842: 0x0004, 0x3843: 0x0004, 0x3844: 0x0004, 0x3845: 0x0004, - 0x3846: 0x0004, 0x3847: 0x0004, 0x3848: 0x0004, 0x3849: 0x0004, 0x384a: 0x0004, 0x384b: 0x0004, - 0x384c: 0x0004, 0x384d: 0x0004, 0x384e: 0x0004, 0x384f: 0x0004, 0x3850: 0x0004, 0x3851: 0x0004, - 0x3852: 0x0004, 0x3853: 0x0004, 0x3854: 0x0004, 0x3855: 0x0004, 0x3856: 0x0004, 0x3857: 0x0004, - 0x3858: 0x0004, 0x3859: 0x0004, 0x385a: 0x0004, 0x385b: 0x0004, 0x385c: 0x0004, 0x385d: 0x0004, - 0x385e: 0x0004, 0x385f: 0x0004, 0x3860: 0x0004, - 0x386d: 0x0004, 0x386e: 0x0004, 0x386f: 0x0004, - 0x3870: 0x0004, 0x3871: 0x0004, 0x3872: 0x0004, 0x3873: 0x0004, 0x3874: 0x0004, 0x3875: 0x0004, - 0x3877: 0x0004, 0x3878: 0x0004, 0x3879: 0x0004, 0x387a: 0x0004, 0x387b: 0x0004, - 0x387c: 0x0004, 0x387d: 0x0004, 0x387e: 0x0004, 0x387f: 0x0004, + 0x3840: 0x0002, 0x3841: 0x0002, 0x3842: 0x0002, 0x3843: 0x0002, 0x3844: 0x0002, 0x3845: 0x0002, + 0x3846: 0x0002, 0x3847: 0x0002, 0x3848: 0x0002, 0x3849: 0x0002, 0x384a: 0x0002, 0x384b: 0x0002, + 0x384c: 0x0002, 0x384d: 0x0002, 0x384e: 0x0002, 0x384f: 0x0002, 0x3850: 0x0002, 0x3851: 0x0002, + 0x3852: 0x0002, 0x3853: 0x0002, 0x3854: 0x0002, 0x3855: 0x0002, 0x3856: 0x0002, 0x3857: 0x0002, + 0x3858: 0x0002, 0x3859: 0x0002, 0x385a: 0x0002, 0x385b: 0x0002, 0x385c: 0x0002, 0x385d: 0x0002, + 0x385e: 0x0002, 0x385f: 0x0002, 0x3860: 0x0002, + 0x386d: 0x0002, 0x386e: 0x0002, 0x386f: 0x0002, + 0x3870: 0x0002, 0x3871: 0x0002, 0x3872: 0x0002, 0x3873: 0x0002, 0x3874: 0x0002, 0x3875: 0x0002, + 0x3877: 0x0002, 0x3878: 0x0002, 0x3879: 0x0002, 0x387a: 0x0002, 0x387b: 0x0002, + 0x387c: 0x0002, 0x387d: 0x0002, 0x387e: 0x0002, 0x387f: 0x0002, // Block 0xe2, offset 0x3880 - 0x3880: 0x0004, 0x3881: 0x0004, 0x3882: 0x0004, 0x3883: 0x0004, 0x3884: 0x0004, 0x3885: 0x0004, - 0x3886: 0x0004, 0x3887: 0x0004, 0x3888: 0x0004, 0x3889: 0x0004, 0x388a: 0x0004, 0x388b: 0x0004, - 0x388c: 0x0004, 0x388d: 0x0004, 0x388e: 0x0004, 0x388f: 0x0004, 0x3890: 0x0004, 0x3891: 0x0004, - 0x3892: 0x0004, 0x3893: 0x0004, 0x3894: 0x0004, 0x3895: 0x0004, 0x3896: 0x0004, 0x3897: 0x0004, - 0x3898: 0x0004, 0x3899: 0x0004, 0x389a: 0x0004, 0x389b: 0x0004, 0x389c: 0x0004, 0x389d: 0x0004, - 0x389e: 0x0004, 0x389f: 0x0004, 0x38a0: 0x0004, 0x38a1: 0x0004, 0x38a2: 0x0004, 0x38a3: 0x0004, - 0x38a4: 0x0004, 0x38a5: 0x0004, 0x38a6: 0x0004, 0x38a7: 0x0004, 0x38a8: 0x0004, 0x38a9: 0x0004, - 0x38aa: 0x0004, 0x38ab: 0x0004, 0x38ac: 0x0004, 0x38ad: 0x0004, 0x38ae: 0x0004, 0x38af: 0x0004, - 0x38b0: 0x0004, 0x38b1: 0x0004, 0x38b2: 0x0004, 0x38b3: 0x0004, 0x38b4: 0x0004, 0x38b5: 0x0004, - 0x38b6: 0x0004, 0x38b7: 0x0004, 0x38b8: 0x0004, 0x38b9: 0x0004, 0x38ba: 0x0004, 0x38bb: 0x0004, - 0x38bc: 0x0004, 0x38be: 0x0004, 0x38bf: 0x0004, + 0x3880: 0x0002, 0x3881: 0x0002, 0x3882: 0x0002, 0x3883: 0x0002, 0x3884: 0x0002, 0x3885: 0x0002, + 0x3886: 0x0002, 0x3887: 0x0002, 0x3888: 0x0002, 0x3889: 0x0002, 0x388a: 0x0002, 0x388b: 0x0002, + 0x388c: 0x0002, 0x388d: 0x0002, 0x388e: 0x0002, 0x388f: 0x0002, 0x3890: 0x0002, 0x3891: 0x0002, + 0x3892: 0x0002, 0x3893: 0x0002, 0x3894: 0x0002, 0x3895: 0x0002, 0x3896: 0x0002, 0x3897: 0x0002, + 0x3898: 0x0002, 0x3899: 0x0002, 0x389a: 0x0002, 0x389b: 0x0002, 0x389c: 0x0002, 0x389d: 0x0002, + 0x389e: 0x0002, 0x389f: 0x0002, 0x38a0: 0x0002, 0x38a1: 0x0002, 0x38a2: 0x0002, 0x38a3: 0x0002, + 0x38a4: 0x0002, 0x38a5: 0x0002, 0x38a6: 0x0002, 0x38a7: 0x0002, 0x38a8: 0x0002, 0x38a9: 0x0002, + 0x38aa: 0x0002, 0x38ab: 0x0002, 0x38ac: 0x0002, 0x38ad: 0x0002, 0x38ae: 0x0002, 0x38af: 0x0002, + 0x38b0: 0x0002, 0x38b1: 0x0002, 0x38b2: 0x0002, 0x38b3: 0x0002, 0x38b4: 0x0002, 0x38b5: 0x0002, + 0x38b6: 0x0002, 0x38b7: 0x0002, 0x38b8: 0x0002, 0x38b9: 0x0002, 0x38ba: 0x0002, 0x38bb: 0x0002, + 0x38bc: 0x0002, 0x38be: 0x0002, 0x38bf: 0x0002, // Block 0xe3, offset 0x38c0 - 0x38c0: 0x0004, 0x38c1: 0x0004, 0x38c2: 0x0004, 0x38c3: 0x0004, 0x38c4: 0x0004, 0x38c5: 0x0004, - 0x38c6: 0x0004, 0x38c7: 0x0004, 0x38c8: 0x0004, 0x38c9: 0x0004, 0x38ca: 0x0004, 0x38cb: 0x0004, - 0x38cc: 0x0004, 0x38cd: 0x0004, 0x38ce: 0x0004, 0x38cf: 0x0004, 0x38d0: 0x0004, 0x38d1: 0x0004, - 0x38d2: 0x0004, 0x38d3: 0x0004, - 0x38e0: 0x0004, 0x38e1: 0x0004, 0x38e2: 0x0004, 0x38e3: 0x0004, - 0x38e4: 0x0004, 0x38e5: 0x0004, 0x38e6: 0x0004, 0x38e7: 0x0004, 0x38e8: 0x0004, 0x38e9: 0x0004, - 0x38ea: 0x0004, 0x38eb: 0x0004, 0x38ec: 0x0004, 0x38ed: 0x0004, 0x38ee: 0x0004, 0x38ef: 0x0004, - 0x38f0: 0x0004, 0x38f1: 0x0004, 0x38f2: 0x0004, 0x38f3: 0x0004, 0x38f4: 0x0004, 0x38f5: 0x0004, - 0x38f6: 0x0004, 0x38f7: 0x0004, 0x38f8: 0x0004, 0x38f9: 0x0004, 0x38fa: 0x0004, 0x38fb: 0x0004, - 0x38fc: 0x0004, 0x38fd: 0x0004, 0x38fe: 0x0004, 0x38ff: 0x0004, + 0x38c0: 0x0002, 0x38c1: 0x0002, 0x38c2: 0x0002, 0x38c3: 0x0002, 0x38c4: 0x0002, 0x38c5: 0x0002, + 0x38c6: 0x0002, 0x38c7: 0x0002, 0x38c8: 0x0002, 0x38c9: 0x0002, 0x38ca: 0x0002, 0x38cb: 0x0002, + 0x38cc: 0x0002, 0x38cd: 0x0002, 0x38ce: 0x0002, 0x38cf: 0x0002, 0x38d0: 0x0002, 0x38d1: 0x0002, + 0x38d2: 0x0002, 0x38d3: 0x0002, + 0x38e0: 0x0002, 0x38e1: 0x0002, 0x38e2: 0x0002, 0x38e3: 0x0002, + 0x38e4: 0x0002, 0x38e5: 0x0002, 0x38e6: 0x0002, 0x38e7: 0x0002, 0x38e8: 0x0002, 0x38e9: 0x0002, + 0x38ea: 0x0002, 0x38eb: 0x0002, 0x38ec: 0x0002, 0x38ed: 0x0002, 0x38ee: 0x0002, 0x38ef: 0x0002, + 0x38f0: 0x0002, 0x38f1: 0x0002, 0x38f2: 0x0002, 0x38f3: 0x0002, 0x38f4: 0x0002, 0x38f5: 0x0002, + 0x38f6: 0x0002, 0x38f7: 0x0002, 0x38f8: 0x0002, 0x38f9: 0x0002, 0x38fa: 0x0002, 0x38fb: 0x0002, + 0x38fc: 0x0002, 0x38fd: 0x0002, 0x38fe: 0x0002, 0x38ff: 0x0002, // Block 0xe4, offset 0x3900 - 0x3900: 0x0004, 0x3901: 0x0004, 0x3902: 0x0004, 0x3903: 0x0004, 0x3904: 0x0004, 0x3905: 0x0004, - 0x3906: 0x0004, 0x3907: 0x0004, 0x3908: 0x0004, 0x3909: 0x0004, 0x390a: 0x0004, - 0x390f: 0x0004, 0x3910: 0x0004, 0x3911: 0x0004, - 0x3912: 0x0004, 0x3913: 0x0004, - 0x3920: 0x0004, 0x3921: 0x0004, 0x3922: 0x0004, 0x3923: 0x0004, - 0x3924: 0x0004, 0x3925: 0x0004, 0x3926: 0x0004, 0x3927: 0x0004, 0x3928: 0x0004, 0x3929: 0x0004, - 0x392a: 0x0004, 0x392b: 0x0004, 0x392c: 0x0004, 0x392d: 0x0004, 0x392e: 0x0004, 0x392f: 0x0004, - 0x3930: 0x0004, 0x3934: 0x0004, - 0x3938: 0x0004, 0x3939: 0x0004, 0x393a: 0x0004, 0x393b: 0x0002, + 0x3900: 0x0002, 0x3901: 0x0002, 0x3902: 0x0002, 0x3903: 0x0002, 0x3904: 0x0002, 0x3905: 0x0002, + 0x3906: 0x0002, 0x3907: 0x0002, 0x3908: 0x0002, 0x3909: 0x0002, 0x390a: 0x0002, + 0x390f: 0x0002, 0x3910: 0x0002, 0x3911: 0x0002, + 0x3912: 0x0002, 0x3913: 0x0002, + 0x3920: 0x0002, 0x3921: 0x0002, 0x3922: 0x0002, 0x3923: 0x0002, + 0x3924: 0x0002, 0x3925: 0x0002, 0x3926: 0x0002, 0x3927: 0x0002, 0x3928: 0x0002, 0x3929: 0x0002, + 0x392a: 0x0002, 0x392b: 0x0002, 0x392c: 0x0002, 0x392d: 0x0002, 0x392e: 0x0002, 0x392f: 0x0002, + 0x3930: 0x0002, 0x3934: 0x0002, + 0x3938: 0x0002, 0x3939: 0x0002, 0x393a: 0x0002, 0x393b: 0x0002, 0x393c: 0x0002, 0x393d: 0x0002, 0x393e: 0x0002, 0x393f: 0x0002, // Block 0xe5, offset 0x3940 - 0x3940: 0x0004, 0x3941: 0x0004, 0x3942: 0x0004, 0x3943: 0x0004, 0x3944: 0x0004, 0x3945: 0x0004, - 0x3946: 0x0004, 0x3947: 0x0004, 0x3948: 0x0004, 0x3949: 0x0004, 0x394a: 0x0004, 0x394b: 0x0004, - 0x394c: 0x0004, 0x394d: 0x0004, 0x394e: 0x0004, 0x394f: 0x0004, 0x3950: 0x0004, 0x3951: 0x0004, - 0x3952: 0x0004, 0x3953: 0x0004, 0x3954: 0x0004, 0x3955: 0x0004, 0x3956: 0x0004, 0x3957: 0x0004, - 0x3958: 0x0004, 0x3959: 0x0004, 0x395a: 0x0004, 0x395b: 0x0004, 0x395c: 0x0004, 0x395d: 0x0004, - 0x395e: 0x0004, 0x395f: 0x0004, 0x3960: 0x0004, 0x3961: 0x0004, 0x3962: 0x0004, 0x3963: 0x0004, - 0x3964: 0x0004, 0x3965: 0x0004, 0x3966: 0x0004, 0x3967: 0x0004, 0x3968: 0x0004, 0x3969: 0x0004, - 0x396a: 0x0004, 0x396b: 0x0004, 0x396c: 0x0004, 0x396d: 0x0004, 0x396e: 0x0004, 0x396f: 0x0004, - 0x3970: 0x0004, 0x3971: 0x0004, 0x3972: 0x0004, 0x3973: 0x0004, 0x3974: 0x0004, 0x3975: 0x0004, - 0x3976: 0x0004, 0x3977: 0x0004, 0x3978: 0x0004, 0x3979: 0x0004, 0x397a: 0x0004, 0x397b: 0x0004, - 0x397c: 0x0004, 0x397d: 0x0004, 0x397e: 0x0004, + 0x3940: 0x0002, 0x3941: 0x0002, 0x3942: 0x0002, 0x3943: 0x0002, 0x3944: 0x0002, 0x3945: 0x0002, + 0x3946: 0x0002, 0x3947: 0x0002, 0x3948: 0x0002, 0x3949: 0x0002, 0x394a: 0x0002, 0x394b: 0x0002, + 0x394c: 0x0002, 0x394d: 0x0002, 0x394e: 0x0002, 0x394f: 0x0002, 0x3950: 0x0002, 0x3951: 0x0002, + 0x3952: 0x0002, 0x3953: 0x0002, 0x3954: 0x0002, 0x3955: 0x0002, 0x3956: 0x0002, 0x3957: 0x0002, + 0x3958: 0x0002, 0x3959: 0x0002, 0x395a: 0x0002, 0x395b: 0x0002, 0x395c: 0x0002, 0x395d: 0x0002, + 0x395e: 0x0002, 0x395f: 0x0002, 0x3960: 0x0002, 0x3961: 0x0002, 0x3962: 0x0002, 0x3963: 0x0002, + 0x3964: 0x0002, 0x3965: 0x0002, 0x3966: 0x0002, 0x3967: 0x0002, 0x3968: 0x0002, 0x3969: 0x0002, + 0x396a: 0x0002, 0x396b: 0x0002, 0x396c: 0x0002, 0x396d: 0x0002, 0x396e: 0x0002, 0x396f: 0x0002, + 0x3970: 0x0002, 0x3971: 0x0002, 0x3972: 0x0002, 0x3973: 0x0002, 0x3974: 0x0002, 0x3975: 0x0002, + 0x3976: 0x0002, 0x3977: 0x0002, 0x3978: 0x0002, 0x3979: 0x0002, 0x397a: 0x0002, 0x397b: 0x0002, + 0x397c: 0x0002, 0x397d: 0x0002, 0x397e: 0x0002, // Block 0xe6, offset 0x3980 - 0x3980: 0x0004, 0x3982: 0x0004, 0x3983: 0x0004, 0x3984: 0x0004, 0x3985: 0x0004, - 0x3986: 0x0004, 0x3987: 0x0004, 0x3988: 0x0004, 0x3989: 0x0004, 0x398a: 0x0004, 0x398b: 0x0004, - 0x398c: 0x0004, 0x398d: 0x0004, 0x398e: 0x0004, 0x398f: 0x0004, 0x3990: 0x0004, 0x3991: 0x0004, - 0x3992: 0x0004, 0x3993: 0x0004, 0x3994: 0x0004, 0x3995: 0x0004, 0x3996: 0x0004, 0x3997: 0x0004, - 0x3998: 0x0004, 0x3999: 0x0004, 0x399a: 0x0004, 0x399b: 0x0004, 0x399c: 0x0004, 0x399d: 0x0004, - 0x399e: 0x0004, 0x399f: 0x0004, 0x39a0: 0x0004, 0x39a1: 0x0004, 0x39a2: 0x0004, 0x39a3: 0x0004, - 0x39a4: 0x0004, 0x39a5: 0x0004, 0x39a6: 0x0004, 0x39a7: 0x0004, 0x39a8: 0x0004, 0x39a9: 0x0004, - 0x39aa: 0x0004, 0x39ab: 0x0004, 0x39ac: 0x0004, 0x39ad: 0x0004, 0x39ae: 0x0004, 0x39af: 0x0004, - 0x39b0: 0x0004, 0x39b1: 0x0004, 0x39b2: 0x0004, 0x39b3: 0x0004, 0x39b4: 0x0004, 0x39b5: 0x0004, - 0x39b6: 0x0004, 0x39b7: 0x0004, 0x39b8: 0x0004, 0x39b9: 0x0004, 0x39ba: 0x0004, 0x39bb: 0x0004, - 0x39bc: 0x0004, 0x39bd: 0x0004, 0x39be: 0x0004, 0x39bf: 0x0004, + 0x3980: 0x0002, 0x3982: 0x0002, 0x3983: 0x0002, 0x3984: 0x0002, 0x3985: 0x0002, + 0x3986: 0x0002, 0x3987: 0x0002, 0x3988: 0x0002, 0x3989: 0x0002, 0x398a: 0x0002, 0x398b: 0x0002, + 0x398c: 0x0002, 0x398d: 0x0002, 0x398e: 0x0002, 0x398f: 0x0002, 0x3990: 0x0002, 0x3991: 0x0002, + 0x3992: 0x0002, 0x3993: 0x0002, 0x3994: 0x0002, 0x3995: 0x0002, 0x3996: 0x0002, 0x3997: 0x0002, + 0x3998: 0x0002, 0x3999: 0x0002, 0x399a: 0x0002, 0x399b: 0x0002, 0x399c: 0x0002, 0x399d: 0x0002, + 0x399e: 0x0002, 0x399f: 0x0002, 0x39a0: 0x0002, 0x39a1: 0x0002, 0x39a2: 0x0002, 0x39a3: 0x0002, + 0x39a4: 0x0002, 0x39a5: 0x0002, 0x39a6: 0x0002, 0x39a7: 0x0002, 0x39a8: 0x0002, 0x39a9: 0x0002, + 0x39aa: 0x0002, 0x39ab: 0x0002, 0x39ac: 0x0002, 0x39ad: 0x0002, 0x39ae: 0x0002, 0x39af: 0x0002, + 0x39b0: 0x0002, 0x39b1: 0x0002, 0x39b2: 0x0002, 0x39b3: 0x0002, 0x39b4: 0x0002, 0x39b5: 0x0002, + 0x39b6: 0x0002, 0x39b7: 0x0002, 0x39b8: 0x0002, 0x39b9: 0x0002, 0x39ba: 0x0002, 0x39bb: 0x0002, + 0x39bc: 0x0002, 0x39bd: 0x0002, 0x39be: 0x0002, 0x39bf: 0x0002, // Block 0xe7, offset 0x39c0 - 0x39c0: 0x0004, 0x39c1: 0x0004, 0x39c2: 0x0004, 0x39c3: 0x0004, 0x39c4: 0x0004, 0x39c5: 0x0004, - 0x39c6: 0x0004, 0x39c7: 0x0004, 0x39c8: 0x0004, 0x39c9: 0x0004, 0x39ca: 0x0004, 0x39cb: 0x0004, - 0x39cc: 0x0004, 0x39cd: 0x0004, 0x39ce: 0x0004, 0x39cf: 0x0004, 0x39d0: 0x0004, 0x39d1: 0x0004, - 0x39d2: 0x0004, 0x39d3: 0x0004, 0x39d4: 0x0004, 0x39d5: 0x0004, 0x39d6: 0x0004, 0x39d7: 0x0004, - 0x39d8: 0x0004, 0x39d9: 0x0004, 0x39da: 0x0004, 0x39db: 0x0004, 0x39dc: 0x0004, 0x39dd: 0x0004, - 0x39de: 0x0004, 0x39df: 0x0004, 0x39e0: 0x0004, 0x39e1: 0x0004, 0x39e2: 0x0004, 0x39e3: 0x0004, - 0x39e4: 0x0004, 0x39e5: 0x0004, 0x39e6: 0x0004, 0x39e7: 0x0004, 0x39e8: 0x0004, 0x39e9: 0x0004, - 0x39ea: 0x0004, 0x39eb: 0x0004, 0x39ec: 0x0004, 0x39ed: 0x0004, 0x39ee: 0x0004, 0x39ef: 0x0004, - 0x39f0: 0x0004, 0x39f1: 0x0004, 0x39f2: 0x0004, 0x39f3: 0x0004, 0x39f4: 0x0004, 0x39f5: 0x0004, - 0x39f6: 0x0004, 0x39f7: 0x0004, 0x39f8: 0x0004, 0x39f9: 0x0004, 0x39fa: 0x0004, 0x39fb: 0x0004, - 0x39fc: 0x0004, 0x39fd: 0x0004, 0x39fe: 0x0004, 0x39ff: 0x0004, + 0x39c0: 0x0002, 0x39c1: 0x0002, 0x39c2: 0x0002, 0x39c3: 0x0002, 0x39c4: 0x0002, 0x39c5: 0x0002, + 0x39c6: 0x0002, 0x39c7: 0x0002, 0x39c8: 0x0002, 0x39c9: 0x0002, 0x39ca: 0x0002, 0x39cb: 0x0002, + 0x39cc: 0x0002, 0x39cd: 0x0002, 0x39ce: 0x0002, 0x39cf: 0x0002, 0x39d0: 0x0002, 0x39d1: 0x0002, + 0x39d2: 0x0002, 0x39d3: 0x0002, 0x39d4: 0x0002, 0x39d5: 0x0002, 0x39d6: 0x0002, 0x39d7: 0x0002, + 0x39d8: 0x0002, 0x39d9: 0x0002, 0x39da: 0x0002, 0x39db: 0x0002, 0x39dc: 0x0002, 0x39dd: 0x0002, + 0x39de: 0x0002, 0x39df: 0x0002, 0x39e0: 0x0002, 0x39e1: 0x0002, 0x39e2: 0x0002, 0x39e3: 0x0002, + 0x39e4: 0x0002, 0x39e5: 0x0002, 0x39e6: 0x0002, 0x39e7: 0x0002, 0x39e8: 0x0002, 0x39e9: 0x0002, + 0x39ea: 0x0002, 0x39eb: 0x0002, 0x39ec: 0x0002, 0x39ed: 0x0002, 0x39ee: 0x0002, 0x39ef: 0x0002, + 0x39f0: 0x0002, 0x39f1: 0x0002, 0x39f2: 0x0002, 0x39f3: 0x0002, 0x39f4: 0x0002, 0x39f5: 0x0002, + 0x39f6: 0x0002, 0x39f7: 0x0002, 0x39f8: 0x0002, 0x39f9: 0x0002, 0x39fa: 0x0002, 0x39fb: 0x0002, + 0x39fc: 0x0002, 0x39ff: 0x0002, // Block 0xe8, offset 0x3a00 - 0x3a00: 0x0004, 0x3a01: 0x0004, 0x3a02: 0x0004, 0x3a03: 0x0004, 0x3a04: 0x0004, 0x3a05: 0x0004, - 0x3a06: 0x0004, 0x3a07: 0x0004, 0x3a08: 0x0004, 0x3a09: 0x0004, 0x3a0a: 0x0004, 0x3a0b: 0x0004, - 0x3a0c: 0x0004, 0x3a0d: 0x0004, 0x3a0e: 0x0004, 0x3a0f: 0x0004, 0x3a10: 0x0004, 0x3a11: 0x0004, - 0x3a12: 0x0004, 0x3a13: 0x0004, 0x3a14: 0x0004, 0x3a15: 0x0004, 0x3a16: 0x0004, 0x3a17: 0x0004, - 0x3a18: 0x0004, 0x3a19: 0x0004, 0x3a1a: 0x0004, 0x3a1b: 0x0004, 0x3a1c: 0x0004, 0x3a1d: 0x0004, - 0x3a1e: 0x0004, 0x3a1f: 0x0004, 0x3a20: 0x0004, 0x3a21: 0x0004, 0x3a22: 0x0004, 0x3a23: 0x0004, - 0x3a24: 0x0004, 0x3a25: 0x0004, 0x3a26: 0x0004, 0x3a27: 0x0004, 0x3a28: 0x0004, 0x3a29: 0x0004, - 0x3a2a: 0x0004, 0x3a2b: 0x0004, 0x3a2c: 0x0004, 0x3a2d: 0x0004, 0x3a2e: 0x0004, 0x3a2f: 0x0004, - 0x3a30: 0x0004, 0x3a31: 0x0004, 0x3a32: 0x0004, 0x3a33: 0x0004, 0x3a34: 0x0004, 0x3a35: 0x0004, - 0x3a36: 0x0004, 0x3a37: 0x0004, 0x3a38: 0x0004, 0x3a39: 0x0004, 0x3a3a: 0x0004, 0x3a3b: 0x0004, - 0x3a3c: 0x0004, 0x3a3f: 0x0004, + 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: 0x0002, 0x3a21: 0x0002, 0x3a22: 0x0002, 0x3a23: 0x0002, + 0x3a24: 0x0002, 0x3a25: 0x0002, 0x3a26: 0x0002, 0x3a27: 0x0002, 0x3a28: 0x0002, 0x3a29: 0x0002, + 0x3a2a: 0x0002, 0x3a2b: 0x0002, 0x3a2c: 0x0002, 0x3a2d: 0x0002, 0x3a2e: 0x0002, 0x3a2f: 0x0002, + 0x3a30: 0x0002, 0x3a31: 0x0002, 0x3a32: 0x0002, 0x3a33: 0x0002, 0x3a34: 0x0002, 0x3a35: 0x0002, + 0x3a36: 0x0002, 0x3a37: 0x0002, 0x3a38: 0x0002, 0x3a39: 0x0002, 0x3a3a: 0x0002, 0x3a3b: 0x0002, + 0x3a3c: 0x0002, 0x3a3d: 0x0002, // Block 0xe9, offset 0x3a40 - 0x3a40: 0x0004, 0x3a41: 0x0004, 0x3a42: 0x0004, 0x3a43: 0x0004, 0x3a44: 0x0004, 0x3a45: 0x0004, - 0x3a46: 0x0004, 0x3a47: 0x0004, 0x3a48: 0x0004, 0x3a49: 0x0004, 0x3a4a: 0x0004, 0x3a4b: 0x0004, - 0x3a4c: 0x0004, 0x3a4d: 0x0004, 0x3a4e: 0x0004, 0x3a4f: 0x0004, 0x3a50: 0x0004, 0x3a51: 0x0004, - 0x3a52: 0x0004, 0x3a53: 0x0004, 0x3a54: 0x0004, 0x3a55: 0x0004, 0x3a56: 0x0004, 0x3a57: 0x0004, - 0x3a58: 0x0004, 0x3a59: 0x0004, 0x3a5a: 0x0004, 0x3a5b: 0x0004, 0x3a5c: 0x0004, 0x3a5d: 0x0004, - 0x3a5e: 0x0004, 0x3a5f: 0x0004, 0x3a60: 0x0004, 0x3a61: 0x0004, 0x3a62: 0x0004, 0x3a63: 0x0004, - 0x3a64: 0x0004, 0x3a65: 0x0004, 0x3a66: 0x0004, 0x3a67: 0x0004, 0x3a68: 0x0004, 0x3a69: 0x0004, - 0x3a6a: 0x0004, 0x3a6b: 0x0004, 0x3a6c: 0x0004, 0x3a6d: 0x0004, 0x3a6e: 0x0004, 0x3a6f: 0x0004, - 0x3a70: 0x0004, 0x3a71: 0x0004, 0x3a72: 0x0004, 0x3a73: 0x0004, 0x3a74: 0x0004, 0x3a75: 0x0004, - 0x3a76: 0x0004, 0x3a77: 0x0004, 0x3a78: 0x0004, 0x3a79: 0x0004, 0x3a7a: 0x0004, 0x3a7b: 0x0004, - 0x3a7c: 0x0004, 0x3a7d: 0x0004, + 0x3a4b: 0x0002, + 0x3a4c: 0x0002, 0x3a4d: 0x0002, 0x3a4e: 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, + 0x3a7a: 0x0002, // Block 0xea, offset 0x3a80 - 0x3a8b: 0x0004, - 0x3a8c: 0x0004, 0x3a8d: 0x0004, 0x3a8e: 0x0004, 0x3a90: 0x0004, 0x3a91: 0x0004, - 0x3a92: 0x0004, 0x3a93: 0x0004, 0x3a94: 0x0004, 0x3a95: 0x0004, 0x3a96: 0x0004, 0x3a97: 0x0004, - 0x3a98: 0x0004, 0x3a99: 0x0004, 0x3a9a: 0x0004, 0x3a9b: 0x0004, 0x3a9c: 0x0004, 0x3a9d: 0x0004, - 0x3a9e: 0x0004, 0x3a9f: 0x0004, 0x3aa0: 0x0004, 0x3aa1: 0x0004, 0x3aa2: 0x0004, 0x3aa3: 0x0004, - 0x3aa4: 0x0004, 0x3aa5: 0x0004, 0x3aa6: 0x0004, 0x3aa7: 0x0004, - 0x3aba: 0x0004, + 0x3a95: 0x0002, 0x3a96: 0x0002, + 0x3aa4: 0x0002, // Block 0xeb, offset 0x3ac0 - 0x3ad5: 0x0004, 0x3ad6: 0x0004, - 0x3ae4: 0x0004, + 0x3afb: 0x0002, + 0x3afc: 0x0002, 0x3afd: 0x0002, 0x3afe: 0x0002, 0x3aff: 0x0002, // Block 0xec, offset 0x3b00 - 0x3b3b: 0x0004, - 0x3b3c: 0x0004, 0x3b3d: 0x0004, 0x3b3e: 0x0004, 0x3b3f: 0x0004, + 0x3b00: 0x0002, 0x3b01: 0x0002, 0x3b02: 0x0002, 0x3b03: 0x0002, 0x3b04: 0x0002, 0x3b05: 0x0002, + 0x3b06: 0x0002, 0x3b07: 0x0002, 0x3b08: 0x0002, 0x3b09: 0x0002, 0x3b0a: 0x0002, 0x3b0b: 0x0002, + 0x3b0c: 0x0002, 0x3b0d: 0x0002, 0x3b0e: 0x0002, 0x3b0f: 0x0002, // Block 0xed, offset 0x3b40 - 0x3b40: 0x0004, 0x3b41: 0x0004, 0x3b42: 0x0004, 0x3b43: 0x0004, 0x3b44: 0x0004, 0x3b45: 0x0004, - 0x3b46: 0x0004, 0x3b47: 0x0004, 0x3b48: 0x0004, 0x3b49: 0x0004, 0x3b4a: 0x0004, 0x3b4b: 0x0004, - 0x3b4c: 0x0004, 0x3b4d: 0x0004, 0x3b4e: 0x0004, 0x3b4f: 0x0004, + 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, + 0x3b5e: 0x0002, 0x3b5f: 0x0002, + 0x3b6b: 0x0002, 0x3b6c: 0x0002, + 0x3b74: 0x0002, 0x3b75: 0x0002, + 0x3b76: 0x0002, 0x3b77: 0x0002, 0x3b78: 0x0002, 0x3b79: 0x0002, 0x3b7a: 0x0002, 0x3b7b: 0x0002, + 0x3b7c: 0x0002, // Block 0xee, offset 0x3b80 - 0x3b80: 0x0004, 0x3b81: 0x0004, 0x3b82: 0x0004, 0x3b83: 0x0004, 0x3b84: 0x0004, 0x3b85: 0x0004, - 0x3b8c: 0x0004, 0x3b90: 0x0004, 0x3b91: 0x0004, - 0x3b92: 0x0004, 0x3b95: 0x0004, 0x3b96: 0x0004, 0x3b97: 0x0004, - 0x3b9c: 0x0004, 0x3b9d: 0x0004, - 0x3b9e: 0x0004, 0x3b9f: 0x0004, - 0x3bab: 0x0004, 0x3bac: 0x0004, - 0x3bb4: 0x0004, 0x3bb5: 0x0004, - 0x3bb6: 0x0004, 0x3bb7: 0x0004, 0x3bb8: 0x0004, 0x3bb9: 0x0004, 0x3bba: 0x0004, 0x3bbb: 0x0004, - 0x3bbc: 0x0004, + 0x3ba0: 0x0002, 0x3ba1: 0x0002, 0x3ba2: 0x0002, 0x3ba3: 0x0002, + 0x3ba4: 0x0002, 0x3ba5: 0x0002, 0x3ba6: 0x0002, 0x3ba7: 0x0002, 0x3ba8: 0x0002, 0x3ba9: 0x0002, + 0x3baa: 0x0002, 0x3bab: 0x0002, + 0x3bb0: 0x0002, // Block 0xef, offset 0x3bc0 - 0x3be0: 0x0004, 0x3be1: 0x0004, 0x3be2: 0x0004, 0x3be3: 0x0004, - 0x3be4: 0x0004, 0x3be5: 0x0004, 0x3be6: 0x0004, 0x3be7: 0x0004, 0x3be8: 0x0004, 0x3be9: 0x0004, - 0x3bea: 0x0004, 0x3beb: 0x0004, - 0x3bf0: 0x0004, + 0x3bcc: 0x0002, 0x3bcd: 0x0002, 0x3bce: 0x0002, 0x3bcf: 0x0002, 0x3bd0: 0x0002, 0x3bd1: 0x0002, + 0x3bd2: 0x0002, 0x3bd3: 0x0002, 0x3bd4: 0x0002, 0x3bd5: 0x0002, 0x3bd6: 0x0002, 0x3bd7: 0x0002, + 0x3bd8: 0x0002, 0x3bd9: 0x0002, 0x3bda: 0x0002, 0x3bdb: 0x0002, 0x3bdc: 0x0002, 0x3bdd: 0x0002, + 0x3bde: 0x0002, 0x3bdf: 0x0002, 0x3be0: 0x0002, 0x3be1: 0x0002, 0x3be2: 0x0002, 0x3be3: 0x0002, + 0x3be4: 0x0002, 0x3be5: 0x0002, 0x3be6: 0x0002, 0x3be7: 0x0002, 0x3be8: 0x0002, 0x3be9: 0x0002, + 0x3bea: 0x0002, 0x3beb: 0x0002, 0x3bec: 0x0002, 0x3bed: 0x0002, 0x3bee: 0x0002, 0x3bef: 0x0002, + 0x3bf0: 0x0002, 0x3bf1: 0x0002, 0x3bf2: 0x0002, 0x3bf3: 0x0002, 0x3bf4: 0x0002, 0x3bf5: 0x0002, + 0x3bf6: 0x0002, 0x3bf7: 0x0002, 0x3bf8: 0x0002, 0x3bf9: 0x0002, 0x3bfa: 0x0002, + 0x3bfc: 0x0002, 0x3bfd: 0x0002, 0x3bfe: 0x0002, 0x3bff: 0x0002, // Block 0xf0, offset 0x3c00 - 0x3c0c: 0x0004, 0x3c0d: 0x0004, 0x3c0e: 0x0004, 0x3c0f: 0x0004, 0x3c10: 0x0004, 0x3c11: 0x0004, - 0x3c12: 0x0004, 0x3c13: 0x0004, 0x3c14: 0x0004, 0x3c15: 0x0004, 0x3c16: 0x0004, 0x3c17: 0x0004, - 0x3c18: 0x0004, 0x3c19: 0x0004, 0x3c1a: 0x0004, 0x3c1b: 0x0004, 0x3c1c: 0x0004, 0x3c1d: 0x0004, - 0x3c1e: 0x0004, 0x3c1f: 0x0004, 0x3c20: 0x0004, 0x3c21: 0x0004, 0x3c22: 0x0004, 0x3c23: 0x0004, - 0x3c24: 0x0004, 0x3c25: 0x0004, 0x3c26: 0x0004, 0x3c27: 0x0004, 0x3c28: 0x0004, 0x3c29: 0x0004, - 0x3c2a: 0x0004, 0x3c2b: 0x0004, 0x3c2c: 0x0004, 0x3c2d: 0x0004, 0x3c2e: 0x0004, 0x3c2f: 0x0004, - 0x3c30: 0x0004, 0x3c31: 0x0004, 0x3c32: 0x0004, 0x3c33: 0x0004, 0x3c34: 0x0004, 0x3c35: 0x0004, - 0x3c36: 0x0004, 0x3c37: 0x0004, 0x3c38: 0x0004, 0x3c39: 0x0004, 0x3c3a: 0x0004, - 0x3c3c: 0x0004, 0x3c3d: 0x0004, 0x3c3e: 0x0004, 0x3c3f: 0x0004, + 0x3c00: 0x0002, 0x3c01: 0x0002, 0x3c02: 0x0002, 0x3c03: 0x0002, 0x3c04: 0x0002, 0x3c05: 0x0002, + 0x3c07: 0x0002, 0x3c08: 0x0002, 0x3c09: 0x0002, 0x3c0a: 0x0002, 0x3c0b: 0x0002, + 0x3c0c: 0x0002, 0x3c0d: 0x0002, 0x3c0e: 0x0002, 0x3c0f: 0x0002, 0x3c10: 0x0002, 0x3c11: 0x0002, + 0x3c12: 0x0002, 0x3c13: 0x0002, 0x3c14: 0x0002, 0x3c15: 0x0002, 0x3c16: 0x0002, 0x3c17: 0x0002, + 0x3c18: 0x0002, 0x3c19: 0x0002, 0x3c1a: 0x0002, 0x3c1b: 0x0002, 0x3c1c: 0x0002, 0x3c1d: 0x0002, + 0x3c1e: 0x0002, 0x3c1f: 0x0002, 0x3c20: 0x0002, 0x3c21: 0x0002, 0x3c22: 0x0002, 0x3c23: 0x0002, + 0x3c24: 0x0002, 0x3c25: 0x0002, 0x3c26: 0x0002, 0x3c27: 0x0002, 0x3c28: 0x0002, 0x3c29: 0x0002, + 0x3c2a: 0x0002, 0x3c2b: 0x0002, 0x3c2c: 0x0002, 0x3c2d: 0x0002, 0x3c2e: 0x0002, 0x3c2f: 0x0002, + 0x3c30: 0x0002, 0x3c31: 0x0002, 0x3c32: 0x0002, 0x3c33: 0x0002, 0x3c34: 0x0002, 0x3c35: 0x0002, + 0x3c36: 0x0002, 0x3c37: 0x0002, 0x3c38: 0x0002, 0x3c39: 0x0002, 0x3c3a: 0x0002, 0x3c3b: 0x0002, + 0x3c3c: 0x0002, 0x3c3d: 0x0002, 0x3c3e: 0x0002, 0x3c3f: 0x0002, // Block 0xf1, offset 0x3c40 - 0x3c40: 0x0004, 0x3c41: 0x0004, 0x3c42: 0x0004, 0x3c43: 0x0004, 0x3c44: 0x0004, 0x3c45: 0x0004, - 0x3c47: 0x0004, 0x3c48: 0x0004, 0x3c49: 0x0004, 0x3c4a: 0x0004, 0x3c4b: 0x0004, - 0x3c4c: 0x0004, 0x3c4d: 0x0004, 0x3c4e: 0x0004, 0x3c4f: 0x0004, 0x3c50: 0x0004, 0x3c51: 0x0004, - 0x3c52: 0x0004, 0x3c53: 0x0004, 0x3c54: 0x0004, 0x3c55: 0x0004, 0x3c56: 0x0004, 0x3c57: 0x0004, - 0x3c58: 0x0004, 0x3c59: 0x0004, 0x3c5a: 0x0004, 0x3c5b: 0x0004, 0x3c5c: 0x0004, 0x3c5d: 0x0004, - 0x3c5e: 0x0004, 0x3c5f: 0x0004, 0x3c60: 0x0004, 0x3c61: 0x0004, 0x3c62: 0x0004, 0x3c63: 0x0004, - 0x3c64: 0x0004, 0x3c65: 0x0004, 0x3c66: 0x0004, 0x3c67: 0x0004, 0x3c68: 0x0004, 0x3c69: 0x0004, - 0x3c6a: 0x0004, 0x3c6b: 0x0004, 0x3c6c: 0x0004, 0x3c6d: 0x0004, 0x3c6e: 0x0004, 0x3c6f: 0x0004, - 0x3c70: 0x0004, 0x3c71: 0x0004, 0x3c72: 0x0004, 0x3c73: 0x0004, 0x3c74: 0x0004, 0x3c75: 0x0004, - 0x3c76: 0x0004, 0x3c77: 0x0004, 0x3c78: 0x0004, 0x3c79: 0x0004, 0x3c7a: 0x0004, 0x3c7b: 0x0004, - 0x3c7c: 0x0004, 0x3c7d: 0x0004, 0x3c7e: 0x0004, 0x3c7f: 0x0004, + 0x3c70: 0x0002, 0x3c71: 0x0002, 0x3c72: 0x0002, 0x3c73: 0x0002, 0x3c74: 0x0002, 0x3c75: 0x0002, + 0x3c76: 0x0002, 0x3c77: 0x0002, 0x3c78: 0x0002, 0x3c79: 0x0002, 0x3c7a: 0x0002, 0x3c7b: 0x0002, + 0x3c7c: 0x0002, // Block 0xf2, offset 0x3c80 - 0x3cb0: 0x0004, 0x3cb1: 0x0004, 0x3cb2: 0x0004, 0x3cb3: 0x0004, 0x3cb4: 0x0004, 0x3cb5: 0x0004, - 0x3cb6: 0x0004, 0x3cb7: 0x0004, 0x3cb8: 0x0004, 0x3cb9: 0x0004, 0x3cba: 0x0004, 0x3cbb: 0x0004, - 0x3cbc: 0x0004, + 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, + 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, + 0x3ca4: 0x0002, 0x3ca5: 0x0002, 0x3ca6: 0x0002, 0x3ca7: 0x0002, 0x3ca8: 0x0002, 0x3ca9: 0x0002, + 0x3caa: 0x0002, 0x3cab: 0x0002, 0x3cac: 0x0002, 0x3cad: 0x0002, 0x3cae: 0x0002, 0x3caf: 0x0002, + 0x3cb0: 0x0002, 0x3cb1: 0x0002, 0x3cb2: 0x0002, 0x3cb3: 0x0002, 0x3cb4: 0x0002, 0x3cb5: 0x0002, + 0x3cb6: 0x0002, 0x3cb7: 0x0002, 0x3cb8: 0x0002, 0x3cb9: 0x0002, 0x3cba: 0x0002, 0x3cbb: 0x0002, + 0x3cbc: 0x0002, 0x3cbd: 0x0002, 0x3cbe: 0x0002, 0x3cbf: 0x0002, // Block 0xf3, offset 0x3cc0 - 0x3cc0: 0x0004, 0x3cc1: 0x0004, 0x3cc2: 0x0004, 0x3cc3: 0x0004, 0x3cc4: 0x0004, 0x3cc5: 0x0004, - 0x3cc6: 0x0004, 0x3cc7: 0x0004, 0x3cc8: 0x0004, 0x3cc9: 0x0004, - 0x3ccf: 0x0004, 0x3cd0: 0x0004, 0x3cd1: 0x0004, - 0x3cd2: 0x0004, 0x3cd3: 0x0004, 0x3cd4: 0x0004, 0x3cd5: 0x0004, 0x3cd6: 0x0004, 0x3cd7: 0x0004, - 0x3cd8: 0x0004, 0x3cd9: 0x0004, 0x3cda: 0x0004, 0x3cdb: 0x0004, 0x3cdc: 0x0004, 0x3cdd: 0x0004, - 0x3cde: 0x0004, 0x3cdf: 0x0004, 0x3ce0: 0x0004, 0x3ce1: 0x0004, 0x3ce2: 0x0004, 0x3ce3: 0x0004, - 0x3ce4: 0x0004, 0x3ce5: 0x0004, 0x3ce6: 0x0004, 0x3ce7: 0x0004, 0x3ce8: 0x0004, 0x3ce9: 0x0004, - 0x3cea: 0x0004, 0x3ceb: 0x0004, 0x3cec: 0x0004, 0x3ced: 0x0004, 0x3cee: 0x0004, 0x3cef: 0x0004, - 0x3cf0: 0x0004, 0x3cf1: 0x0004, 0x3cf2: 0x0004, 0x3cf3: 0x0004, 0x3cf4: 0x0004, 0x3cf5: 0x0004, - 0x3cf6: 0x0004, 0x3cf7: 0x0004, 0x3cf8: 0x0004, 0x3cf9: 0x0004, 0x3cfa: 0x0004, 0x3cfb: 0x0004, - 0x3cfc: 0x0004, 0x3cfd: 0x0004, 0x3cfe: 0x0004, 0x3cff: 0x0004, + 0x3cc0: 0x0002, 0x3cc1: 0x0002, 0x3cc2: 0x0002, 0x3cc3: 0x0002, 0x3cc4: 0x0002, 0x3cc5: 0x0002, + 0x3cc6: 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, + 0x3cf0: 0x0002, 0x3cf1: 0x0002, 0x3cf2: 0x0002, 0x3cf3: 0x0002, 0x3cf4: 0x0002, 0x3cf5: 0x0002, + 0x3cf6: 0x0002, 0x3cf7: 0x0002, 0x3cf8: 0x0002, // Block 0xf4, offset 0x3d00 - 0x3d00: 0x0004, 0x3d01: 0x0004, 0x3d02: 0x0004, 0x3d03: 0x0004, 0x3d04: 0x0004, 0x3d05: 0x0004, - 0x3d06: 0x0004, - 0x3d0e: 0x0004, 0x3d0f: 0x0004, 0x3d10: 0x0004, 0x3d11: 0x0004, - 0x3d12: 0x0004, 0x3d13: 0x0004, 0x3d14: 0x0004, 0x3d15: 0x0004, 0x3d16: 0x0004, 0x3d17: 0x0004, - 0x3d18: 0x0004, 0x3d19: 0x0004, 0x3d1a: 0x0004, 0x3d1b: 0x0004, 0x3d1c: 0x0004, - 0x3d1f: 0x0004, 0x3d20: 0x0004, 0x3d21: 0x0004, 0x3d22: 0x0004, 0x3d23: 0x0004, - 0x3d24: 0x0004, 0x3d25: 0x0004, 0x3d26: 0x0004, 0x3d27: 0x0004, 0x3d28: 0x0004, 0x3d29: 0x0004, - 0x3d30: 0x0004, 0x3d31: 0x0004, 0x3d32: 0x0004, 0x3d33: 0x0004, 0x3d34: 0x0004, 0x3d35: 0x0004, - 0x3d36: 0x0004, 0x3d37: 0x0004, 0x3d38: 0x0004, + 0x3d01: 0x0001, + 0x3d20: 0x0001, 0x3d21: 0x0001, 0x3d22: 0x0001, 0x3d23: 0x0001, + 0x3d24: 0x0001, 0x3d25: 0x0001, 0x3d26: 0x0001, 0x3d27: 0x0001, 0x3d28: 0x0001, 0x3d29: 0x0001, + 0x3d2a: 0x0001, 0x3d2b: 0x0001, 0x3d2c: 0x0001, 0x3d2d: 0x0001, 0x3d2e: 0x0001, 0x3d2f: 0x0001, + 0x3d30: 0x0001, 0x3d31: 0x0001, 0x3d32: 0x0001, 0x3d33: 0x0001, 0x3d34: 0x0001, 0x3d35: 0x0001, + 0x3d36: 0x0001, 0x3d37: 0x0001, 0x3d38: 0x0001, 0x3d39: 0x0001, 0x3d3a: 0x0001, 0x3d3b: 0x0001, + 0x3d3c: 0x0001, 0x3d3d: 0x0001, 0x3d3e: 0x0001, 0x3d3f: 0x0001, // Block 0xf5, offset 0x3d40 - 0x3d40: 0x0002, 0x3d41: 0x0002, 0x3d42: 0x0002, 0x3d43: 0x0002, 0x3d44: 0x0002, 0x3d45: 0x0002, - 0x3d46: 0x0002, 0x3d47: 0x0002, 0x3d48: 0x0002, 0x3d49: 0x0002, 0x3d4a: 0x0002, 0x3d4b: 0x0002, - 0x3d4c: 0x0002, 0x3d4d: 0x0002, 0x3d4e: 0x0002, 0x3d4f: 0x0002, 0x3d50: 0x0002, 0x3d51: 0x0002, - 0x3d52: 0x0002, 0x3d53: 0x0002, 0x3d54: 0x0002, 0x3d55: 0x0002, 0x3d56: 0x0002, 0x3d57: 0x0002, - 0x3d58: 0x0002, 0x3d59: 0x0002, 0x3d5a: 0x0002, 0x3d5b: 0x0002, 0x3d5c: 0x0002, 0x3d5d: 0x0002, - 0x3d5e: 0x0002, 0x3d5f: 0x0002, 0x3d60: 0x0002, 0x3d61: 0x0002, 0x3d62: 0x0002, 0x3d63: 0x0002, - 0x3d64: 0x0002, 0x3d65: 0x0002, 0x3d66: 0x0002, 0x3d67: 0x0002, 0x3d68: 0x0002, 0x3d69: 0x0002, - 0x3d6a: 0x0002, 0x3d6b: 0x0002, 0x3d6c: 0x0002, 0x3d6d: 0x0002, 0x3d6e: 0x0002, 0x3d6f: 0x0002, - 0x3d70: 0x0002, 0x3d71: 0x0002, 0x3d72: 0x0002, 0x3d73: 0x0002, 0x3d74: 0x0002, 0x3d75: 0x0002, - 0x3d76: 0x0002, 0x3d77: 0x0002, 0x3d78: 0x0002, 0x3d79: 0x0002, 0x3d7a: 0x0002, 0x3d7b: 0x0002, - 0x3d7c: 0x0002, 0x3d7d: 0x0002, - // Block 0xf6, offset 0x3d80 - 0x3d81: 0x0001, - 0x3da0: 0x0001, 0x3da1: 0x0001, 0x3da2: 0x0001, 0x3da3: 0x0001, - 0x3da4: 0x0001, 0x3da5: 0x0001, 0x3da6: 0x0001, 0x3da7: 0x0001, 0x3da8: 0x0001, 0x3da9: 0x0001, - 0x3daa: 0x0001, 0x3dab: 0x0001, 0x3dac: 0x0001, 0x3dad: 0x0001, 0x3dae: 0x0001, 0x3daf: 0x0001, - 0x3db0: 0x0001, 0x3db1: 0x0001, 0x3db2: 0x0001, 0x3db3: 0x0001, 0x3db4: 0x0001, 0x3db5: 0x0001, - 0x3db6: 0x0001, 0x3db7: 0x0001, 0x3db8: 0x0001, 0x3db9: 0x0001, 0x3dba: 0x0001, 0x3dbb: 0x0001, - 0x3dbc: 0x0001, 0x3dbd: 0x0001, 0x3dbe: 0x0001, 0x3dbf: 0x0001, - // Block 0xf7, offset 0x3dc0 - 0x3dc0: 0x0003, 0x3dc1: 0x0003, 0x3dc2: 0x0003, 0x3dc3: 0x0003, 0x3dc4: 0x0003, 0x3dc5: 0x0003, - 0x3dc6: 0x0003, 0x3dc7: 0x0003, 0x3dc8: 0x0003, 0x3dc9: 0x0003, 0x3dca: 0x0003, 0x3dcb: 0x0003, - 0x3dcc: 0x0003, 0x3dcd: 0x0003, 0x3dce: 0x0003, 0x3dcf: 0x0003, 0x3dd0: 0x0003, 0x3dd1: 0x0003, - 0x3dd2: 0x0003, 0x3dd3: 0x0003, 0x3dd4: 0x0003, 0x3dd5: 0x0003, 0x3dd6: 0x0003, 0x3dd7: 0x0003, - 0x3dd8: 0x0003, 0x3dd9: 0x0003, 0x3dda: 0x0003, 0x3ddb: 0x0003, 0x3ddc: 0x0003, 0x3ddd: 0x0003, - 0x3dde: 0x0003, 0x3ddf: 0x0003, 0x3de0: 0x0003, 0x3de1: 0x0003, 0x3de2: 0x0003, 0x3de3: 0x0003, - 0x3de4: 0x0003, 0x3de5: 0x0003, 0x3de6: 0x0003, 0x3de7: 0x0003, 0x3de8: 0x0003, 0x3de9: 0x0003, - 0x3dea: 0x0003, 0x3deb: 0x0003, 0x3dec: 0x0003, 0x3ded: 0x0003, 0x3dee: 0x0003, 0x3def: 0x0003, - 0x3df0: 0x0003, 0x3df1: 0x0003, 0x3df2: 0x0003, 0x3df3: 0x0003, 0x3df4: 0x0003, 0x3df5: 0x0003, - 0x3df6: 0x0003, 0x3df7: 0x0003, 0x3df8: 0x0003, 0x3df9: 0x0003, 0x3dfa: 0x0003, 0x3dfb: 0x0003, - 0x3dfc: 0x0003, 0x3dfd: 0x0003, + 0x3d40: 0x0003, 0x3d41: 0x0003, 0x3d42: 0x0003, 0x3d43: 0x0003, 0x3d44: 0x0003, 0x3d45: 0x0003, + 0x3d46: 0x0003, 0x3d47: 0x0003, 0x3d48: 0x0003, 0x3d49: 0x0003, 0x3d4a: 0x0003, 0x3d4b: 0x0003, + 0x3d4c: 0x0003, 0x3d4d: 0x0003, 0x3d4e: 0x0003, 0x3d4f: 0x0003, 0x3d50: 0x0003, 0x3d51: 0x0003, + 0x3d52: 0x0003, 0x3d53: 0x0003, 0x3d54: 0x0003, 0x3d55: 0x0003, 0x3d56: 0x0003, 0x3d57: 0x0003, + 0x3d58: 0x0003, 0x3d59: 0x0003, 0x3d5a: 0x0003, 0x3d5b: 0x0003, 0x3d5c: 0x0003, 0x3d5d: 0x0003, + 0x3d5e: 0x0003, 0x3d5f: 0x0003, 0x3d60: 0x0003, 0x3d61: 0x0003, 0x3d62: 0x0003, 0x3d63: 0x0003, + 0x3d64: 0x0003, 0x3d65: 0x0003, 0x3d66: 0x0003, 0x3d67: 0x0003, 0x3d68: 0x0003, 0x3d69: 0x0003, + 0x3d6a: 0x0003, 0x3d6b: 0x0003, 0x3d6c: 0x0003, 0x3d6d: 0x0003, 0x3d6e: 0x0003, 0x3d6f: 0x0003, + 0x3d70: 0x0003, 0x3d71: 0x0003, 0x3d72: 0x0003, 0x3d73: 0x0003, 0x3d74: 0x0003, 0x3d75: 0x0003, + 0x3d76: 0x0003, 0x3d77: 0x0003, 0x3d78: 0x0003, 0x3d79: 0x0003, 0x3d7a: 0x0003, 0x3d7b: 0x0003, + 0x3d7c: 0x0003, 0x3d7d: 0x0003, } // stringWidthIndex: 30 blocks, 1920 entries, 1920 bytes @@ -1683,10 +1655,10 @@ var stringWidthIndex = [1920]uint8{ // Block 0x17, offset 0x5c0 0x5c0: 0xd7, 0x5c3: 0xd8, 0x5c4: 0xd9, 0x5c5: 0xda, 0x5c6: 0xdb, 0x5c7: 0xdc, 0x5c8: 0xdd, 0x5c9: 0xde, 0x5cc: 0xdf, 0x5cd: 0xe0, 0x5ce: 0xe1, 0x5cf: 0xe2, - 0x5d0: 0xe3, 0x5d1: 0xe4, 0x5d2: 0xe5, 0x5d3: 0xe6, 0x5d4: 0xe7, 0x5d5: 0xe8, 0x5d6: 0xe9, 0x5d7: 0xea, - 0x5d8: 0xe5, 0x5d9: 0xeb, 0x5da: 0xe5, 0x5db: 0xec, 0x5df: 0xed, - 0x5e4: 0xee, 0x5e5: 0xef, 0x5e6: 0xe5, 0x5e7: 0xe5, - 0x5e9: 0xf0, 0x5ea: 0xf1, 0x5eb: 0xf2, + 0x5d0: 0xe3, 0x5d1: 0xe4, 0x5d2: 0x39, 0x5d3: 0xe5, 0x5d4: 0xe6, 0x5d5: 0xe7, 0x5d6: 0xe8, 0x5d7: 0xe9, + 0x5d8: 0x39, 0x5d9: 0xea, 0x5da: 0x39, 0x5db: 0xeb, 0x5df: 0xec, + 0x5e4: 0xed, 0x5e5: 0xee, 0x5e6: 0x39, 0x5e7: 0x39, + 0x5e9: 0xef, 0x5ea: 0xf0, 0x5eb: 0xf1, // Block 0x18, offset 0x600 0x600: 0x39, 0x601: 0x39, 0x602: 0x39, 0x603: 0x39, 0x604: 0x39, 0x605: 0x39, 0x606: 0x39, 0x607: 0x39, 0x608: 0x39, 0x609: 0x39, 0x60a: 0x39, 0x60b: 0x39, 0x60c: 0x39, 0x60d: 0x39, 0x60e: 0x39, 0x60f: 0x39, @@ -1695,7 +1667,7 @@ var stringWidthIndex = [1920]uint8{ 0x620: 0x39, 0x621: 0x39, 0x622: 0x39, 0x623: 0x39, 0x624: 0x39, 0x625: 0x39, 0x626: 0x39, 0x627: 0x39, 0x628: 0x39, 0x629: 0x39, 0x62a: 0x39, 0x62b: 0x39, 0x62c: 0x39, 0x62d: 0x39, 0x62e: 0x39, 0x62f: 0x39, 0x630: 0x39, 0x631: 0x39, 0x632: 0x39, 0x633: 0x39, 0x634: 0x39, 0x635: 0x39, 0x636: 0x39, 0x637: 0x39, - 0x638: 0x39, 0x639: 0x39, 0x63a: 0x39, 0x63b: 0x39, 0x63c: 0x39, 0x63d: 0x39, 0x63e: 0x39, 0x63f: 0xf3, + 0x638: 0x39, 0x639: 0x39, 0x63a: 0x39, 0x63b: 0x39, 0x63c: 0x39, 0x63d: 0x39, 0x63e: 0x39, 0x63f: 0xe6, // Block 0x19, offset 0x640 0x650: 0x0b, 0x651: 0x0c, 0x653: 0x0d, 0x656: 0x0e, 0x657: 0x06, 0x658: 0x0f, 0x65a: 0x10, 0x65b: 0x11, 0x65c: 0x12, 0x65d: 0x13, 0x65e: 0x14, 0x65f: 0x15, @@ -1704,7 +1676,7 @@ var stringWidthIndex = [1920]uint8{ 0x670: 0x06, 0x671: 0x06, 0x672: 0x06, 0x673: 0x06, 0x674: 0x06, 0x675: 0x06, 0x676: 0x06, 0x677: 0x06, 0x678: 0x06, 0x679: 0x06, 0x67a: 0x06, 0x67b: 0x06, 0x67c: 0x06, 0x67d: 0x06, 0x67e: 0x06, 0x67f: 0x16, // Block 0x1a, offset 0x680 - 0x680: 0xf4, 0x681: 0x08, 0x684: 0x08, 0x685: 0x08, 0x686: 0x08, 0x687: 0x09, + 0x680: 0xf2, 0x681: 0x08, 0x684: 0x08, 0x685: 0x08, 0x686: 0x08, 0x687: 0x09, // Block 0x1b, offset 0x6c0 0x6c0: 0x5b, 0x6c1: 0x5b, 0x6c2: 0x5b, 0x6c3: 0x5b, 0x6c4: 0x5b, 0x6c5: 0x5b, 0x6c6: 0x5b, 0x6c7: 0x5b, 0x6c8: 0x5b, 0x6c9: 0x5b, 0x6ca: 0x5b, 0x6cb: 0x5b, 0x6cc: 0x5b, 0x6cd: 0x5b, 0x6ce: 0x5b, 0x6cf: 0x5b, @@ -1713,7 +1685,7 @@ var stringWidthIndex = [1920]uint8{ 0x6e0: 0x5b, 0x6e1: 0x5b, 0x6e2: 0x5b, 0x6e3: 0x5b, 0x6e4: 0x5b, 0x6e5: 0x5b, 0x6e6: 0x5b, 0x6e7: 0x5b, 0x6e8: 0x5b, 0x6e9: 0x5b, 0x6ea: 0x5b, 0x6eb: 0x5b, 0x6ec: 0x5b, 0x6ed: 0x5b, 0x6ee: 0x5b, 0x6ef: 0x5b, 0x6f0: 0x5b, 0x6f1: 0x5b, 0x6f2: 0x5b, 0x6f3: 0x5b, 0x6f4: 0x5b, 0x6f5: 0x5b, 0x6f6: 0x5b, 0x6f7: 0x5b, - 0x6f8: 0x5b, 0x6f9: 0x5b, 0x6fa: 0x5b, 0x6fb: 0x5b, 0x6fc: 0x5b, 0x6fd: 0x5b, 0x6fe: 0x5b, 0x6ff: 0xf5, + 0x6f8: 0x5b, 0x6f9: 0x5b, 0x6fa: 0x5b, 0x6fb: 0x5b, 0x6fc: 0x5b, 0x6fd: 0x5b, 0x6fe: 0x5b, 0x6ff: 0xf3, // Block 0x1c, offset 0x700 0x720: 0x18, 0x730: 0x09, 0x731: 0x09, 0x732: 0x09, 0x733: 0x09, 0x734: 0x09, 0x735: 0x09, 0x736: 0x09, 0x737: 0x09, diff --git a/vendor/github.com/clipperhouse/displaywidth/width.go b/vendor/github.com/clipperhouse/displaywidth/width.go index 55dd458..a96ab0b 100644 --- a/vendor/github.com/clipperhouse/displaywidth/width.go +++ b/vendor/github.com/clipperhouse/displaywidth/width.go @@ -122,28 +122,18 @@ func graphemeWidth[T stringish.Interface](s T, options Options) int { prop := property(p) // Variation Selector 16 (VS16) requests emoji presentation - if sz > 0 && len(s) >= sz+3 { + if prop != _Wide && sz > 0 && len(s) >= sz+3 { vs := s[sz : sz+3] if isVS16(vs) { - prop = _Emoji + prop = _Wide } // VS15 (0x8E) requests text presentation but does not affect width, // in my reading of Unicode TR51. Falls through to return the base // character's property. } - /* - Note: we previously had some regional indicator handling here, - intending to treat single RI's as width 1 and pairs as width 2. - We think that's what the Unicode #11 indicates? - - Then we looked at what actual terminals do, and they seem to treat - single and paired RI's as width 2, regardless. See terminal-test/. - Looks like VS Code does the same FWIW. - */ - if options.EastAsianWidth && prop == _East_Asian_Ambiguous { - prop = _East_Asian_Wide + prop = _Wide } if prop > upperBound { @@ -167,13 +157,11 @@ func isVS16[T stringish.Interface](s T) bool { } // propertyWidths is a jump table of sorts, instead of a switch -var propertyWidths = [6]int{ +var propertyWidths = [4]int{ _Default: 1, _Zero_Width: 0, - _East_Asian_Wide: 2, + _Wide: 2, _East_Asian_Ambiguous: 1, - _Emoji: 2, - _Regional_Indicator: 2, } const upperBound = property(len(propertyWidths) - 1) diff --git a/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml b/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml index f2058cc..5b30cd6 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml +++ b/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml @@ -1,5 +1,41 @@ version: "2" + +run: + timeout: 5m + linters: exclusions: presets: - std-error-handling + enable: + - gosec # Detects security problems. + # Keep all extras disabled for now to focus on the integer overflow problem. + # TODO: enable these and other good linters + - dogsled # Detects assignments with too many blank identifiers. + - errcheck + - errchkjson # Detects unsupported types passed to json encoding functions and reports if checks for the returned error can be omitted. + - exhaustive # Detects missing options in enum switch statements. + - gocyclo + - govet + - ineffassign + - makezero # Finds slice declarations with non-zero initial length. + - misspell # Detects commonly misspelled English words in comments. + - nakedret # Detects uses of naked returns. + - prealloc # Detects slice declarations that could potentially be pre-allocated. + - predeclared # Detects code that shadows one of Go's predeclared identifiers. + - reassign # Detects reassigning a top-level variable in another package. + - staticcheck + - thelper # Detects test helpers without t.Helper(). + - tparallel # Detects inappropriate usage of t.Parallel(). + - unconvert # Detects unnecessary type conversions. + - unused + - usestdlibvars # Detects the possibility to use variables/constants from the Go standard library. + - usetesting # Reports uses of functions with replacement inside the testing package. + settings: + govet: + disable: + - stdversion + gosec: + excludes: + - G404 # Weak random number generator used in tests. + - G304 # File inclusion diff --git a/vendor/github.com/gabriel-vasile/mimetype/README.md b/vendor/github.com/gabriel-vasile/mimetype/README.md index ea5c89a..9fe71ac 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/README.md +++ b/vendor/github.com/gabriel-vasile/mimetype/README.md @@ -70,13 +70,13 @@ If increasing the limit does not help, please ## Tests In addition to unit tests, [mimetype_tests](https://github.com/gabriel-vasile/mimetype_tests) compares the -library with the [Unix file utility](https://en.wikipedia.org/wiki/File_(command)) +library with [libmagic](https://en.wikipedia.org/wiki/File_(command)) for around 50 000 sample files. Check the latest comparison results [here](https://github.com/gabriel-vasile/mimetype_tests/actions). ## Benchmarks -Benchmarks for each file format are performed when a PR is open. The results can -be seen on the [workflows page](https://github.com/gabriel-vasile/mimetype/actions/workflows/benchmark.yml). +Benchmarks are performed when a PR is open. The results can be seen on the +[workflows page](https://github.com/gabriel-vasile/mimetype/actions/workflows/benchmark.yml). Performance improvements are welcome but correctness is prioritized. ## Structure diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go b/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go index d11c0a8..fc3c772 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go @@ -94,20 +94,6 @@ func eq(path1, path2 [][]byte) bool { return true } -// LooksLikeObjectOrArray reports if first non white space character from raw -// is either { or [. Parsing raw as JSON is a heavy operation. When receiving some -// text input we can skip parsing if the input does not even look like JSON. -func LooksLikeObjectOrArray(raw []byte) bool { - for i := range raw { - if isSpace(raw[i]) { - continue - } - return raw[i] == '{' || raw[i] == '[' - } - - return false -} - // Parse will take out a parser from the pool depending on queryType and tries // to parse raw bytes as JSON. func Parse(queryType string, raw []byte) (parsed, inspected, firstToken int, querySatisfied bool) { diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/archive.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/archive.go index 8a0c849..0b4c62f 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/archive.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/archive.go @@ -106,10 +106,10 @@ func CRX(raw []byte, limit uint32) bool { if len(raw) < minHeaderLen || !bytes.HasPrefix(raw, []byte("Cr24")) { return false } - pubkeyLen := binary.LittleEndian.Uint32(raw[8:12]) - sigLen := binary.LittleEndian.Uint32(raw[12:16]) + pubkeyLen := int64(binary.LittleEndian.Uint32(raw[8:12])) + sigLen := int64(binary.LittleEndian.Uint32(raw[12:16])) zipOffset := minHeaderLen + pubkeyLen + sigLen - if uint32(len(raw)) < zipOffset { + if zipOffset < 0 || int64(len(raw)) < zipOffset { return false } return Zip(raw[zipOffset:], limit) @@ -209,3 +209,13 @@ func tarChksum(b []byte) (unsigned, signed int64) { } return unsigned, signed } + +// Zlib matches zlib compressed files. +func Zlib(raw []byte, _ uint32) bool { + // https://www.ietf.org/rfc/rfc6713.txt + // This check has one fault: ASCII code can satisfy it; for ex: []byte("x ") + zlib := len(raw) > 1 && + raw[0] == 'x' && binary.BigEndian.Uint16(raw)%31 == 0 + // Check that the file is not a regular text to avoid false positives. + return zlib && !Text(raw, 0) +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/meteo.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/meteo.go new file mode 100644 index 0000000..da77d0b --- /dev/null +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/meteo.go @@ -0,0 +1,12 @@ +package magic + +import "bytes" + +// GRIB matches a GRIdded Binary meteorological file. +// https://www.nco.ncep.noaa.gov/pmb/docs/on388/ +// https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/ +func GRIB(raw []byte, _ uint32) bool { + return len(raw) > 7 && + bytes.HasPrefix(raw, []byte("GRIB")) && + (raw[7] == 1 || raw[7] == 2) +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go index bd4a0c3..e689e92 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go @@ -192,7 +192,8 @@ func matchOleClsid(in []byte, clsid []byte) bool { // Expected offset of CLSID for root storage object. clsidOffset := sectorLength*(1+firstSecID) + 80 - if len(in) <= clsidOffset+16 { + // #731 offset is outside in or wrapped around due to integer overflow. + if len(in) <= clsidOffset+16 || clsidOffset < 0 { return false } diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go index fb84e85..82f6c67 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go @@ -283,7 +283,7 @@ func Shell(raw []byte, _ uint32) bool { // Text matches a plain text file. // // TODO: This function does not parse BOM-less UTF16 and UTF32 files. Not really -// sure it should. Linux file utility also requires a BOM for UTF16 and UTF32. +// sure it should. libmagic also requires a BOM for UTF16 and UTF32. func Text(raw []byte, _ uint32) bool { // First look for BOM. if cset := charset.FromBOM(raw); cset != "" { @@ -352,13 +352,20 @@ func GLTF(raw []byte, limit uint32) bool { return jsonHelper(raw, limit, json.QueryGLTF, json.TokObject) } -func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool { - if !json.LooksLikeObjectOrArray(raw) { +func jsonHelper(raw scan.Bytes, limit uint32, q string, wantToks ...int) bool { + firstNonWS := raw.FirstNonWS() + + hasTargetTok := false + for _, t := range wantToks { + hasTargetTok = hasTargetTok || (t&json.TokArray > 0 && firstNonWS == '[') + hasTargetTok = hasTargetTok || (t&json.TokObject > 0 && firstNonWS == '{') + } + if !hasTargetTok { return false } lraw := len(raw) - parsed, inspected, firstToken, querySatisfied := json.Parse(q, raw) - if !querySatisfied || firstToken&wantTok == 0 { + parsed, inspected, _, querySatisfied := json.Parse(q, raw) + if !querySatisfied { return false } @@ -369,7 +376,7 @@ func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool { // If a section of the file was provided, check if all of it was inspected. // In other words, check that if there was a problem parsing, that problem - // occured at the last byte in the input. + // occurred at the last byte in the input. return inspected == lraw && lraw > 0 } @@ -536,3 +543,57 @@ func Vtt(raw []byte, limit uint32) bool { return bytes.Equal(raw, []byte{0xEF, 0xBB, 0xBF, 0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) || // UTF-8 BOM and "WEBVTT" bytes.Equal(raw, []byte{0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) // "WEBVTT" } + +type rfc822Hint struct { + h []byte + matchFlags scan.Flags +} + +// The hints come from libmagic, but the implementation is bit different. libmagic +// only checks if the file starts with the hint, while we additionally look for +// a secondary hint in the first few lines of input. +func RFC822(raw []byte, limit uint32) bool { + b := scan.Bytes(raw) + + // Keep hints here to avoid instantiating them several times in lineHasRFC822Hint. + // The alternative is to make them a package level var, but then they'd go + // on the heap. + // Some of the hints are IgnoreCase, some not. I selected based on what libmagic + // does and based on personal observations from sample files. + hints := []rfc822Hint{ + {[]byte("From: "), 0}, + {[]byte("To: "), 0}, + {[]byte("CC: "), scan.IgnoreCase}, + {[]byte("Date: "), 0}, + {[]byte("Subject: "), 0}, + {[]byte("Received: "), 0}, + {[]byte("Relay-Version: "), 0}, + {[]byte("#! rnews"), 0}, + {[]byte("N#! rnews"), 0}, + {[]byte("Forward to"), 0}, + {[]byte("Pipe to"), 0}, + {[]byte("DELIVERED-TO: "), scan.IgnoreCase}, + {[]byte("RETURN-PATH: "), scan.IgnoreCase}, + {[]byte("Content-Type: "), 0}, + {[]byte("Content-Transfer-Encoding: "), 0}, + } + if !lineHasRFC822Hint(b.Line(), hints) { + return false + } + for i := 0; i < 20; i++ { + if lineHasRFC822Hint(b.Line(), hints) { + return true + } + } + + return false +} + +func lineHasRFC822Hint(b scan.Bytes, hints []rfc822Hint) bool { + for _, h := range hints { + if b.Match(h.h, h.matchFlags) > -1 { + return true + } + } + return false +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go index 830c5ed..23e30da 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go @@ -69,9 +69,9 @@ func isFileTypeNamePresent(in []byte, flType string) bool { // vintWidth parses the variable-integer width in matroska containers func vintWidth(v int) int { - mask, max, num := 128, 8, 1 - for num < max && v&mask == 0 { - mask = mask >> 1 + mask, nTimes, num := 128, 8, 1 + for num < nTimes && v&mask == 0 { + mask >>= 1 num++ } return num diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go index 31f4248..f3bfa2a 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go @@ -85,10 +85,14 @@ func Zip(raw []byte, limit uint32) bool { // (instead of relying on offsets told by the file.) func Jar(raw []byte, limit uint32) bool { return executableJar(raw) || + // First entry must be an empty META-INF directory or the manifest. + // There is no specification saying that, but the jar reader and writer + // implementations from Java do it that way. + // https://github.com/openjdk/jdk/blob/88c4678eed818cbe9380f35352e90883fed27d33/src/java.base/share/classes/java/util/jar/JarInputStream.java#L170-L173 zipHas(raw, zipEntries{{ - name: []byte("META-INF/MANIFEST.MF"), - }, { name: []byte("META-INF/"), + }, { + name: []byte("META-INF/MANIFEST.MF"), }}, 1) } @@ -127,11 +131,14 @@ type zipEntries []struct { func (z zipEntries) match(file []byte) bool { for i := range z { - if z[i].dir && bytes.HasPrefix(file, z[i].name) { - return true - } - if bytes.Equal(file, z[i].name) { - return true + if z[i].dir { + if bytes.HasPrefix(file, z[i].name) { + return true + } + } else { + if bytes.Equal(file, z[i].name) { + return true + } } } return false diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go b/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go index b19a949..552b4ea 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go @@ -35,6 +35,19 @@ func (b *Bytes) TrimRWS() { } } +// FirstNonWS returns the first non-whitespace character from b, +// or 0x00 if no such character is found. +func (b Bytes) FirstNonWS() byte { + for i := range b { + if ByteIsWS(b[i]) { + continue + } + return b[i] + } + + return 0x00 +} + // Peek one byte from b or 0x00 if b is empty. func (b *Bytes) Peek() byte { if len(*b) > 0 { @@ -63,8 +76,8 @@ func (b *Bytes) PopN(n int) []byte { return nil } -// PopUntil will advance b until, but not including, the first occurence of stopAt -// character. If no occurence is found, then it will advance until the end of b. +// PopUntil will advance b until, but not including, the first occurrence of stopAt +// character. If no occurrence is found, then it will advance until the end of b. // The returned Bytes is a slice of all the bytes that we're advanced over. func (b *Bytes) PopUntil(stopAt ...byte) Bytes { if len(*b) == 0 { @@ -77,7 +90,7 @@ func (b *Bytes) PopUntil(stopAt ...byte) Bytes { prefix := (*b)[:i] *b = (*b)[i:] - return Bytes(prefix) + return prefix } // ReadSlice is the same as PopUntil, but the returned value includes stopAt as well. @@ -94,7 +107,7 @@ func (b *Bytes) ReadSlice(stopAt byte) Bytes { prefix := (*b)[:i] *b = (*b)[i:] - return Bytes(prefix) + return prefix } // Line returns the first line from b and advances b with the length of the @@ -117,7 +130,7 @@ func (b *Bytes) Line() Bytes { // If b length is less than readLimit, it means we received an incomplete file // and proceed with dropping the last line. func (b *Bytes) DropLastLine(readLimit uint32) { - if readLimit == 0 || uint32(len(*b)) < readLimit { + if readLimit == 0 || uint64(len(*b)) < uint64(readLimit) { return } @@ -151,7 +164,7 @@ const ( FullWord ) -// Search for occurences of pattern p inside b at any index. +// Search for occurrences of pattern p inside b at any index. // It returns the index where p was found in b and how many bytes were needed // for matching the pattern. func (b Bytes) Search(p []byte, flags Flags) (i int, l int) { diff --git a/vendor/github.com/gabriel-vasile/mimetype/mime.go b/vendor/github.com/gabriel-vasile/mimetype/mime.go index ec2bc5b..3dadf72 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/mime.go +++ b/vendor/github.com/gabriel-vasile/mimetype/mime.go @@ -2,6 +2,7 @@ package mimetype import ( "mime" + "slices" "strings" "github.com/gabriel-vasile/mimetype/internal/charset" @@ -58,10 +59,8 @@ func (m *MIME) Is(expectedMIME string) bool { return true } - for _, alias := range m.aliases { - if alias == expectedMIME { - return true - } + if slices.Contains(m.aliases, expectedMIME) { + return true } return false @@ -180,10 +179,8 @@ func (m *MIME) lookup(mime string) *MIME { if mime == m.mime { return m } - for _, n := range m.aliases { - if n == mime { - return m - } + if slices.Contains(m.aliases, mime) { + return m } for _, c := range m.children { diff --git a/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md b/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md index 241a81f..45de7b9 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md +++ b/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md @@ -1,4 +1,4 @@ -## 192 Supported MIME types +## 195 Supported MIME types This file is automatically generated when running tests. Do not edit manually. Extension | MIME type
Aliases | Hierarchy @@ -152,6 +152,8 @@ Extension | MIME type
Aliases | Hierarchy **.chm** | **application/vnd.ms-htmlhelp** | chm>root **.wpd** | **application/vnd.wordperfect** | wpd>root **.dxf** | **image/vnd.dxf** | dxf>root +**.grb** | **application/grib** | grb>root +**n/a** | **application/zlib** | zlib>root **.txt** | **text/plain** | txt>root **.svg** | **image/svg+xml** | svg>txt>root **.html** | **text/html** | html>txt>root @@ -195,3 +197,4 @@ Extension | MIME type
Aliases | Hierarchy **.pgm** | **image/x-portable-graymap** | pgm>txt>root **.ppm** | **image/x-portable-pixmap** | ppm>txt>root **.pam** | **image/x-portable-arbitrarymap** | pam>txt>root +**.eml** | **message/rfc822** | eml>txt>root diff --git a/vendor/github.com/gabriel-vasile/mimetype/tree.go b/vendor/github.com/gabriel-vasile/mimetype/tree.go index 21b70d5..29ef820 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/tree.go +++ b/vendor/github.com/gabriel-vasile/mimetype/tree.go @@ -24,7 +24,7 @@ var root = newMIME("application/octet-stream", "", woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, cbor, sqlite3, dwg, nes, lnk, macho, qcp, icns, hdr, mrc, mdb, accdb, zstd, cab, rpm, xz, lzip, torrent, cpio, tzif, xcf, pat, gbr, glb, cabIS, jxr, parquet, - oneNote, chm, wpd, dxf, + oneNote, chm, wpd, dxf, grib, zlib, // Keep text last because it is the slowest check. text, ) @@ -82,7 +82,7 @@ var ( alias("application/x-ogg") oggAudio = newMIME("audio/ogg", ".oga", magic.OggAudio) oggVideo = newMIME("video/ogg", ".ogv", magic.OggVideo) - text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam) + text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam, rfc822) xml = newMIME("text/xml", ".xml", magic.XML, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf, xfdf, owl2, xhtml). alias("application/xml") xhtml = newMIME("application/xhtml+xml", ".html", magic.XHTML) @@ -287,4 +287,7 @@ var ( chm = newMIME("application/vnd.ms-htmlhelp", ".chm", magic.CHM) wpd = newMIME("application/vnd.wordperfect", ".wpd", magic.WPD) dxf = newMIME("image/vnd.dxf", ".dxf", magic.DXF) + rfc822 = newMIME("message/rfc822", ".eml", magic.RFC822) + grib = newMIME("application/grib", ".grb", magic.GRIB) + zlib = newMIME("application/zlib", "", magic.Zlib) ) diff --git a/vendor/go.mau.fi/util/dbutil/upgrades.go b/vendor/go.mau.fi/util/dbutil/upgrades.go index d2aa88f..9536a27 100644 --- a/vendor/go.mau.fi/util/dbutil/upgrades.go +++ b/vendor/go.mau.fi/util/dbutil/upgrades.go @@ -33,6 +33,18 @@ var ErrForeignTables = errors.New("the database contains foreign tables") var ErrNotOwned = errors.New("the database is owned by") var ErrUnsupportedDialect = errors.New("unsupported database dialect") +type NotOwnedError struct { + Owner string +} + +func (e NotOwnedError) Error() string { + return fmt.Sprintf("%v %s", ErrNotOwned, e.Owner) +} + +func (e NotOwnedError) Unwrap() error { + return ErrNotOwned +} + func DangerousInternalUpgradeVersionTable(ctx context.Context, db *Database) error { return db.upgradeVersionTable(ctx) } @@ -143,7 +155,7 @@ func (db *Database) checkDatabaseOwner(ctx context.Context) error { } else if err != nil { return fmt.Errorf("failed to check database owner: %w", err) } else if owner != db.Owner { - return fmt.Errorf("%w %s", ErrNotOwned, owner) + return NotOwnedError{owner} } return nil } diff --git a/vendor/go.mau.fi/util/exsync/syncmap.go b/vendor/go.mau.fi/util/exsync/syncmap.go index b2013f2..f49a57a 100644 --- a/vendor/go.mau.fi/util/exsync/syncmap.go +++ b/vendor/go.mau.fi/util/exsync/syncmap.go @@ -82,6 +82,23 @@ func (sm *Map[Key, Value]) GetDefault(key Key, def Value) Value { return def } +// GetOrSetFactory gets a value in the map if the key already exists, +// otherwise inserts a new value from the given function and returns it. +func (sm *Map[Key, Value]) GetOrSetFactory(key Key, def func() Value) Value { + val, ok := sm.Get(key) + if ok { + return val + } + sm.lock.Lock() + defer sm.lock.Unlock() + value, ok := sm.data[key] + if !ok { + value = def() + sm.data[key] = value + } + return value +} + // GetOrSet gets a value in the map if the key already exists, otherwise inserts the given value and returns it. // // The boolean return parameter is true if the key already exists, and false if the given value was inserted. @@ -120,6 +137,21 @@ func (sm *Map[Key, Value]) CopyFrom(other map[Key]Value) { sm.lock.Unlock() } +// SwapData replaces the internal map with the given map, returning the previous map. +// If the given map is nil, a new empty map is created. +// The given map must not be modified after passing it to this function. +func (sm *Map[Key, Value]) SwapData(other map[Key]Value) map[Key]Value { + sm.lock.Lock() + prev := sm.data + if other == nil { + sm.data = make(map[Key]Value) + } else { + sm.data = other + } + sm.lock.Unlock() + return prev +} + // Clone returns a copy of the map. func (sm *Map[Key, Value]) Clone() *Map[Key, Value] { return NewMapWithData(sm.CopyData()) diff --git a/vendor/golang.org/x/tools/go/ast/astutil/imports.go b/vendor/golang.org/x/tools/go/ast/astutil/imports.go index 5bacc0f..adb4711 100644 --- a/vendor/golang.org/x/tools/go/ast/astutil/imports.go +++ b/vendor/golang.org/x/tools/go/ast/astutil/imports.go @@ -9,6 +9,7 @@ import ( "fmt" "go/ast" "go/token" + "reflect" "slices" "strconv" "strings" @@ -149,7 +150,7 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added if newImport.Name != nil { newImport.Name.NamePos = pos } - newImport.Path.ValuePos = pos + updateBasicLitPos(newImport.Path, pos) newImport.EndPos = pos // Clean up parens. impDecl contains at least one spec. @@ -184,7 +185,7 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added first.Lparen = first.Pos() // Move the imports of the other import declaration to the first one. for _, spec := range gen.Specs { - spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + updateBasicLitPos(spec.(*ast.ImportSpec).Path, first.Pos()) first.Specs = append(first.Specs, spec) } f.Decls = slices.Delete(f.Decls, i, i+1) @@ -470,3 +471,17 @@ func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec { return groups } + +// updateBasicLitPos updates lit.Pos, +// ensuring that lit.End (if set) is displaced by the same amount. +// (See https://go.dev/issue/76395.) +func updateBasicLitPos(lit *ast.BasicLit, pos token.Pos) { + len := lit.End() - lit.Pos() + lit.ValuePos = pos + // TODO(adonovan): after go1.26, simplify to: + // lit.ValueEnd = pos + len + v := reflect.ValueOf(lit).Elem().FieldByName("ValueEnd") + if v.IsValid() && v.Int() != 0 { + v.SetInt(int64(pos + len)) + } +} diff --git a/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml b/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml index 0b9785a..616fccb 100644 --- a/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml +++ b/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace exclude_types: [markdown] @@ -9,7 +9,7 @@ repos: - id: check-added-large-files - repo: https://github.com/tekwizely/pre-commit-golang - rev: v1.0.0-rc.1 + rev: v1.0.0-rc.4 hooks: - id: go-imports-repo args: @@ -18,8 +18,7 @@ repos: - "-w" - id: go-vet-repo-mod - id: go-mod-tidy - # TODO enable this - #- id: go-staticcheck-repo-mod + - id: go-staticcheck-repo-mod - repo: https://github.com/beeper/pre-commit-go rev: v0.4.2 diff --git a/vendor/maunium.net/go/mautrix/CHANGELOG.md b/vendor/maunium.net/go/mautrix/CHANGELOG.md index b6c0ff7..0fb1a10 100644 --- a/vendor/maunium.net/go/mautrix/CHANGELOG.md +++ b/vendor/maunium.net/go/mautrix/CHANGELOG.md @@ -1,3 +1,35 @@ +## v0.26.1 (2025-12-16) + +* **Breaking change *(mediaproxy)*** Changed `GetMediaResponseFile` to return + the mime type from the callback rather than in the return get media return + value. The callback can now also redirect the caller to a different file. +* *(federation)* Added join/knock/leave functions + (thanks to [@nexy7574] in [#422]). +* *(federation/eventauth)* Fixed various incorrect checks. +* *(client)* Added backoff for retrying media uploads to external URLs + (with MSC3870). +* *(bridgev2/config)* Added support for overriding config fields using + environment variables. +* *(bridgev2/commands)* Added command to mute chat on remote network. +* *(bridgev2)* Added interface for network connectors to redirect to a different + user ID when handling an invite from Matrix. +* *(bridgev2)* Added interface for signaling message request status of portals. +* *(bridgev2)* Changed portal creation to not backfill unless `CanBackfill` flag + is set in chat info. +* *(bridgev2)* Changed Matrix reaction handling to only delete old reaction if + bridging the new one is successful. +* *(bridgev2/mxmain)* Improved error message when trying to run bridge with + pre-megabridge database when no database migration exists. +* *(bridgev2)* Improved reliability of database migration when enabling split + portals. +* *(bridgev2)* Improved detection of orphaned DM rooms when starting new chats. +* *(bridgev2)* Stopped sending redundant invites when joining ghosts to public + portal rooms. +* *(bridgev2)* Stopped hardcoding room versions in favor of checking + server capabilities to determine appropriate `/createRoom` parameters. + +[#422]: https://github.com/mautrix/go/pull/422 + ## v0.26.0 (2025-11-16) * *(client,appservice)* Deprecated `SendMassagedStateEvent` as `SendStateEvent` @@ -43,7 +75,7 @@ * *(federation)* Fixed validating auth for requests with query params. * *(federation/eventauth)* Fixed typo causing restricted joins to not work. -[MSC416]: https://github.com/matrix-org/matrix-spec-proposals/pull/4169 +[MSC4169]: https://github.com/matrix-org/matrix-spec-proposals/pull/4169 [#411]: github.com/mautrix/go/pull/411 [#420]: github.com/mautrix/go/pull/420 [#426]: github.com/mautrix/go/pull/426 diff --git a/vendor/maunium.net/go/mautrix/client.go b/vendor/maunium.net/go/mautrix/client.go index d07bede..9073758 100644 --- a/vendor/maunium.net/go/mautrix/client.go +++ b/vendor/maunium.net/go/mautrix/client.go @@ -614,7 +614,9 @@ func (cli *Client) doRetry( select { case <-time.After(backoff): case <-req.Context().Done(): - return nil, nil, req.Context().Err() + if !errors.Is(context.Cause(req.Context()), ErrContextCancelRetry) { + return nil, nil, req.Context().Err() + } } if cli.UpdateRequestOnRetry != nil { req = cli.UpdateRequestOnRetry(req, cause) @@ -740,12 +742,15 @@ func (cli *Client) executeCompiledRequest( cli.RequestStart(req) startTime := time.Now() res, err := client.Do(req) - duration := time.Now().Sub(startTime) + duration := time.Since(startTime) if res != nil && !dontReadResponse { defer res.Body.Close() } if err != nil { - if retries > 0 && !errors.Is(err, context.Canceled) { + // Either error is *not* canceled or the underlying cause of cancelation explicitly asks to retry + canRetry := !errors.Is(err, context.Canceled) || + errors.Is(context.Cause(req.Context()), ErrContextCancelRetry) + if retries > 0 && canRetry { return cli.doRetry( req, err, retries, backoff, responseJSON, handler, dontReadResponse, sizeLimit, client, ) @@ -857,7 +862,7 @@ func (cli *Client) FullSyncRequest(ctx context.Context, req ReqSync) (resp *Resp } start := time.Now() _, err = cli.MakeFullRequest(ctx, fullReq) - duration := time.Now().Sub(start) + duration := time.Since(start) timeout := time.Duration(req.Timeout) * time.Millisecond buffer := 10 * time.Second if req.Since == "" { @@ -961,7 +966,7 @@ func (cli *Client) RegisterGuest(ctx context.Context, req *ReqRegister) (*RespRe // } // token := res.AccessToken func (cli *Client) RegisterDummy(ctx context.Context, req *ReqRegister) (*RespRegister, error) { - res, uia, err := cli.Register(ctx, req) + _, uia, err := cli.Register(ctx, req) if err != nil && uia == nil { return nil, err } else if uia == nil { @@ -970,7 +975,7 @@ func (cli *Client) RegisterDummy(ctx context.Context, req *ReqRegister) (*RespRe return nil, errors.New("server does not support m.login.dummy") } req.Auth = BaseAuthData{Type: AuthTypeDummy, Session: uia.Session} - res, _, err = cli.Register(ctx, req) + res, _, err := cli.Register(ctx, req) if err != nil { return nil, err } @@ -1746,6 +1751,8 @@ func parseRoomStateArray(req *http.Request, res *http.Response, responseJSON any return nil, nil } +type RoomStateMap = map[event.Type]map[string]*event.Event + // State gets all state in a room. // See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidstate func (cli *Client) State(ctx context.Context, roomID id.RoomID) (stateMap RoomStateMap, err error) { @@ -2014,8 +2021,16 @@ func (cli *Client) uploadMediaToURL(ctx context.Context, data ReqUploadMedia) (* Msg("Error uploading media to external URL, not retrying") return nil, err } - cli.Log.Warn().Str("url", data.UnstableUploadURL).Err(err). + backoff := time.Second * time.Duration(cli.DefaultHTTPRetries-retries) + cli.Log.Warn().Err(err). + Str("url", data.UnstableUploadURL). + Int("retry_in_seconds", int(backoff.Seconds())). Msg("Error uploading media to external URL, retrying") + select { + case <-time.After(backoff): + case <-ctx.Done(): + return nil, ctx.Err() + } retries-- _, err = readerSeeker.Seek(0, io.SeekStart) if err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go b/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go index 155cca5..727aacb 100644 --- a/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go +++ b/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go @@ -21,13 +21,24 @@ import ( ) var ( - HashMismatch = errors.New("mismatching SHA-256 digest") - UnsupportedVersion = errors.New("unsupported Matrix file encryption version") - UnsupportedAlgorithm = errors.New("unsupported JWK encryption algorithm") - InvalidKey = errors.New("failed to decode key") - InvalidInitVector = errors.New("failed to decode initialization vector") - InvalidHash = errors.New("failed to decode SHA-256 hash") - ReaderClosed = errors.New("encrypting reader was already closed") + ErrHashMismatch = errors.New("mismatching SHA-256 digest") + ErrUnsupportedVersion = errors.New("unsupported Matrix file encryption version") + ErrUnsupportedAlgorithm = errors.New("unsupported JWK encryption algorithm") + ErrInvalidKey = errors.New("failed to decode key") + ErrInvalidInitVector = errors.New("failed to decode initialization vector") + ErrInvalidHash = errors.New("failed to decode SHA-256 hash") + ErrReaderClosed = errors.New("encrypting reader was already closed") +) + +// Deprecated: use variables prefixed with Err +var ( + HashMismatch = ErrHashMismatch + UnsupportedVersion = ErrUnsupportedVersion + UnsupportedAlgorithm = ErrUnsupportedAlgorithm + InvalidKey = ErrInvalidKey + InvalidInitVector = ErrInvalidInitVector + InvalidHash = ErrInvalidHash + ReaderClosed = ErrReaderClosed ) var ( @@ -85,25 +96,25 @@ func (ef *EncryptedFile) decodeKeys(includeHash bool) error { if ef.decoded != nil { return nil } else if len(ef.Key.Key) != keyBase64Length { - return InvalidKey + return ErrInvalidKey } else if len(ef.InitVector) != ivBase64Length { - return InvalidInitVector + return ErrInvalidInitVector } else if includeHash && len(ef.Hashes.SHA256) != hashBase64Length { - return InvalidHash + return ErrInvalidHash } ef.decoded = &decodedKeys{} _, err := base64.RawURLEncoding.Decode(ef.decoded.key[:], []byte(ef.Key.Key)) if err != nil { - return InvalidKey + return ErrInvalidKey } _, err = base64.RawStdEncoding.Decode(ef.decoded.iv[:], []byte(ef.InitVector)) if err != nil { - return InvalidInitVector + return ErrInvalidInitVector } if includeHash { _, err = base64.RawStdEncoding.Decode(ef.decoded.sha256[:], []byte(ef.Hashes.SHA256)) if err != nil { - return InvalidHash + return ErrInvalidHash } } return nil @@ -179,7 +190,7 @@ var _ io.ReadSeekCloser = (*encryptingReader)(nil) func (r *encryptingReader) Seek(offset int64, whence int) (int64, error) { if r.closed { - return 0, ReaderClosed + return 0, ErrReaderClosed } if offset != 0 || whence != io.SeekStart { return 0, fmt.Errorf("attachments.EncryptStream: only seeking to the beginning is supported") @@ -200,7 +211,7 @@ func (r *encryptingReader) Seek(offset int64, whence int) (int64, error) { func (r *encryptingReader) Read(dst []byte) (n int, err error) { if r.closed { - return 0, ReaderClosed + return 0, ErrReaderClosed } else if r.isDecrypting && r.file.decoded == nil { if err = r.file.PrepareForDecryption(); err != nil { return @@ -224,7 +235,7 @@ func (r *encryptingReader) Close() (err error) { } if r.isDecrypting { if !hmac.Equal(r.hash.Sum(nil), r.file.decoded.sha256[:]) { - return HashMismatch + return ErrHashMismatch } } else { r.file.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(r.hash.Sum(nil)) @@ -265,9 +276,9 @@ func (ef *EncryptedFile) Decrypt(ciphertext []byte) ([]byte, error) { // DecryptInPlace will always call this automatically, so calling this manually is not necessary when using that function. func (ef *EncryptedFile) PrepareForDecryption() error { if ef.Version != "v2" { - return UnsupportedVersion + return ErrUnsupportedVersion } else if ef.Key.Algorithm != "A256CTR" { - return UnsupportedAlgorithm + return ErrUnsupportedAlgorithm } else if err := ef.decodeKeys(true); err != nil { return err } @@ -281,7 +292,7 @@ func (ef *EncryptedFile) DecryptInPlace(data []byte) error { } dataHash := sha256.Sum256(data) if !hmac.Equal(ef.decoded.sha256[:], dataHash[:]) { - return HashMismatch + return ErrHashMismatch } utils.XorA256CTR(data, ef.decoded.key, ef.decoded.iv) return nil diff --git a/vendor/maunium.net/go/mautrix/crypto/cross_sign_pubkey.go b/vendor/maunium.net/go/mautrix/crypto/cross_sign_pubkey.go index f85d1ea..223fc7b 100644 --- a/vendor/maunium.net/go/mautrix/crypto/cross_sign_pubkey.go +++ b/vendor/maunium.net/go/mautrix/crypto/cross_sign_pubkey.go @@ -63,8 +63,8 @@ func (mach *OlmMachine) GetCrossSigningPublicKeys(ctx context.Context, userID id if len(dbKeys) > 0 { masterKey, ok := dbKeys[id.XSUsageMaster] if ok { - selfSigning, _ := dbKeys[id.XSUsageSelfSigning] - userSigning, _ := dbKeys[id.XSUsageUserSigning] + selfSigning := dbKeys[id.XSUsageSelfSigning] + userSigning := dbKeys[id.XSUsageUserSigning] return &CrossSigningPublicKeysCache{ MasterKey: masterKey.Key, SelfSigningKey: selfSigning.Key, diff --git a/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go b/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go index d30b7e3..57406b1 100644 --- a/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go +++ b/vendor/maunium.net/go/mautrix/crypto/cross_sign_store.go @@ -26,24 +26,22 @@ func (mach *OlmMachine) storeCrossSigningKeys(ctx context.Context, crossSigningK log.Error().Err(err). Msg("Error fetching current cross-signing keys of user") } - if currentKeys != nil { - for curKeyUsage, curKey := range currentKeys { - log := log.With().Stringer("old_key", curKey.Key).Str("old_key_usage", string(curKeyUsage)).Logger() - // got a new key with the same usage as an existing key - for _, newKeyUsage := range userKeys.Usage { - if newKeyUsage == curKeyUsage { - if _, ok := userKeys.Keys[id.NewKeyID(id.KeyAlgorithmEd25519, curKey.Key.String())]; !ok { - // old key is not in the new key map, so we drop signatures made by it - if count, err := mach.CryptoStore.DropSignaturesByKey(ctx, userID, curKey.Key); err != nil { - log.Error().Err(err).Msg("Error deleting old signatures made by user") - } else { - log.Debug(). - Int64("signature_count", count). - Msg("Dropped signatures made by old key as it has been replaced") - } + for curKeyUsage, curKey := range currentKeys { + log := log.With().Stringer("old_key", curKey.Key).Str("old_key_usage", string(curKeyUsage)).Logger() + // got a new key with the same usage as an existing key + for _, newKeyUsage := range userKeys.Usage { + if newKeyUsage == curKeyUsage { + if _, ok := userKeys.Keys[id.NewKeyID(id.KeyAlgorithmEd25519, curKey.Key.String())]; !ok { + // old key is not in the new key map, so we drop signatures made by it + if count, err := mach.CryptoStore.DropSignaturesByKey(ctx, userID, curKey.Key); err != nil { + log.Error().Err(err).Msg("Error deleting old signatures made by user") + } else { + log.Debug(). + Int64("signature_count", count). + Msg("Dropped signatures made by old key as it has been replaced") } - break } + break } } } diff --git a/vendor/maunium.net/go/mautrix/crypto/cryptohelper/cryptohelper.go b/vendor/maunium.net/go/mautrix/crypto/cryptohelper/cryptohelper.go index 1939ea7..b62dc12 100644 --- a/vendor/maunium.net/go/mautrix/crypto/cryptohelper/cryptohelper.go +++ b/vendor/maunium.net/go/mautrix/crypto/cryptohelper/cryptohelper.go @@ -278,7 +278,7 @@ func (helper *CryptoHelper) verifyDeviceKeysOnServer(ctx context.Context) error } } -var NoSessionFound = crypto.NoSessionFound +var NoSessionFound = crypto.ErrNoSessionFound const initialSessionWaitTimeout = 3 * time.Second const extendedSessionWaitTimeout = 22 * time.Second @@ -371,6 +371,7 @@ func (helper *CryptoHelper) waitLongerForSession(ctx context.Context, evt *event content := evt.Content.AsEncrypted() log.Debug().Int("wait_seconds", int(extendedSessionWaitTimeout.Seconds())).Msg("Couldn't find session, requesting keys and waiting longer...") + //lint:ignore SA1019 RequestSession will gracefully request from all devices if DeviceID is blank go helper.RequestSession(context.TODO(), evt.RoomID, content.SenderKey, content.SessionID, evt.Sender, content.DeviceID) if !helper.mach.WaitForSession(ctx, evt.RoomID, content.SenderKey, content.SessionID, extendedSessionWaitTimeout) { @@ -418,7 +419,7 @@ func (helper *CryptoHelper) EncryptWithStateKey(ctx context.Context, roomID id.R defer helper.lock.RUnlock() encrypted, err = helper.mach.EncryptMegolmEventWithStateKey(ctx, roomID, evtType, stateKey, content) if err != nil { - if !errors.Is(err, crypto.SessionExpired) && err != crypto.NoGroupSession && !errors.Is(err, crypto.SessionNotShared) { + if !errors.Is(err, crypto.ErrSessionExpired) && err != crypto.ErrNoGroupSession && !errors.Is(err, crypto.ErrSessionNotShared) { return } helper.log.Debug(). diff --git a/vendor/maunium.net/go/mautrix/crypto/decryptmegolm.go b/vendor/maunium.net/go/mautrix/crypto/decryptmegolm.go index 4727947..d8b419a 100644 --- a/vendor/maunium.net/go/mautrix/crypto/decryptmegolm.go +++ b/vendor/maunium.net/go/mautrix/crypto/decryptmegolm.go @@ -24,13 +24,24 @@ import ( ) var ( - IncorrectEncryptedContentType = errors.New("event content is not instance of *event.EncryptedEventContent") - NoSessionFound = errors.New("failed to decrypt megolm event: no session with given ID found") - DuplicateMessageIndex = errors.New("duplicate megolm message index") - WrongRoom = errors.New("encrypted megolm event is not intended for this room") - DeviceKeyMismatch = errors.New("device keys in event and verified device info do not match") - SenderKeyMismatch = errors.New("sender keys in content and megolm session do not match") - RatchetError = errors.New("failed to ratchet session after use") + ErrIncorrectEncryptedContentType = errors.New("event content is not instance of *event.EncryptedEventContent") + ErrNoSessionFound = errors.New("failed to decrypt megolm event: no session with given ID found") + ErrDuplicateMessageIndex = errors.New("duplicate megolm message index") + ErrWrongRoom = errors.New("encrypted megolm event is not intended for this room") + ErrDeviceKeyMismatch = errors.New("device keys in event and verified device info do not match") + ErrSenderKeyMismatch = errors.New("sender keys in content and megolm session do not match") + ErrRatchetError = errors.New("failed to ratchet session after use") +) + +// Deprecated: use variables prefixed with Err +var ( + IncorrectEncryptedContentType = ErrIncorrectEncryptedContentType + NoSessionFound = ErrNoSessionFound + DuplicateMessageIndex = ErrDuplicateMessageIndex + WrongRoom = ErrWrongRoom + DeviceKeyMismatch = ErrDeviceKeyMismatch + SenderKeyMismatch = ErrSenderKeyMismatch + RatchetError = ErrRatchetError ) type megolmEvent struct { @@ -49,9 +60,9 @@ var ( func (mach *OlmMachine) DecryptMegolmEvent(ctx context.Context, evt *event.Event) (*event.Event, error) { content, ok := evt.Content.Parsed.(*event.EncryptedEventContent) if !ok { - return nil, IncorrectEncryptedContentType + return nil, ErrIncorrectEncryptedContentType } else if content.Algorithm != id.AlgorithmMegolmV1 { - return nil, UnsupportedAlgorithm + return nil, ErrUnsupportedAlgorithm } log := mach.machOrContextLog(ctx).With(). Str("action", "decrypt megolm event"). @@ -97,7 +108,7 @@ func (mach *OlmMachine) DecryptMegolmEvent(ctx context.Context, evt *event.Event Msg("Couldn't resolve trust level of session: sent by unknown device") trustLevel = id.TrustStateUnknownDevice } else if device.SigningKey != sess.SigningKey || device.IdentityKey != sess.SenderKey { - return nil, DeviceKeyMismatch + return nil, ErrDeviceKeyMismatch } else { trustLevel, err = mach.ResolveTrustContext(ctx, device) if err != nil { @@ -147,7 +158,7 @@ func (mach *OlmMachine) DecryptMegolmEvent(ctx context.Context, evt *event.Event if err != nil { return nil, fmt.Errorf("failed to parse megolm payload: %w", err) } else if megolmEvt.RoomID != encryptionRoomID { - return nil, WrongRoom + return nil, ErrWrongRoom } if evt.StateKey != nil && megolmEvt.StateKey != nil && mach.AllowEncryptedState { megolmEvt.Type.Class = event.StateEventType @@ -201,19 +212,19 @@ func (mach *OlmMachine) checkUndecryptableMessageIndexDuplication(ctx context.Co messageIndex, decodeErr := ParseMegolmMessageIndex(content.MegolmCiphertext) if decodeErr != nil { log.Warn().Err(decodeErr).Msg("Failed to parse message index to check if it's a duplicate for message that failed to decrypt") - return 0, fmt.Errorf("%w (also failed to parse message index)", olm.UnknownMessageIndex) + return 0, fmt.Errorf("%w (also failed to parse message index)", olm.ErrUnknownMessageIndex) } firstKnown := sess.Internal.FirstKnownIndex() log = log.With().Uint("message_index", messageIndex).Uint32("first_known_index", firstKnown).Logger() if ok, err := mach.CryptoStore.ValidateMessageIndex(ctx, sess.SenderKey, content.SessionID, evt.ID, messageIndex, evt.Timestamp); err != nil { log.Debug().Err(err).Msg("Failed to check if message index is duplicate") - return messageIndex, fmt.Errorf("%w (failed to check if index is duplicate; received: %d, earliest known: %d)", olm.UnknownMessageIndex, messageIndex, firstKnown) + return messageIndex, fmt.Errorf("%w (failed to check if index is duplicate; received: %d, earliest known: %d)", olm.ErrUnknownMessageIndex, messageIndex, firstKnown) } else if !ok { log.Debug().Msg("Failed to decrypt message due to unknown index and found duplicate") - return messageIndex, fmt.Errorf("%w %d (also failed to decrypt because earliest known index is %d)", DuplicateMessageIndex, messageIndex, firstKnown) + return messageIndex, fmt.Errorf("%w %d (also failed to decrypt because earliest known index is %d)", ErrDuplicateMessageIndex, messageIndex, firstKnown) } log.Debug().Msg("Failed to decrypt message due to unknown index, but index is not duplicate") - return messageIndex, fmt.Errorf("%w (not duplicate index; received: %d, earliest known: %d)", olm.UnknownMessageIndex, messageIndex, firstKnown) + return messageIndex, fmt.Errorf("%w (not duplicate index; received: %d, earliest known: %d)", olm.ErrUnknownMessageIndex, messageIndex, firstKnown) } func (mach *OlmMachine) actuallyDecryptMegolmEvent(ctx context.Context, evt *event.Event, encryptionRoomID id.RoomID, content *event.EncryptedEventContent) (*InboundGroupSession, []byte, uint, error) { @@ -224,13 +235,13 @@ func (mach *OlmMachine) actuallyDecryptMegolmEvent(ctx context.Context, evt *eve if err != nil { return nil, nil, 0, fmt.Errorf("failed to get group session: %w", err) } else if sess == nil { - return nil, nil, 0, fmt.Errorf("%w (ID %s)", NoSessionFound, content.SessionID) + return nil, nil, 0, fmt.Errorf("%w (ID %s)", ErrNoSessionFound, content.SessionID) } else if content.SenderKey != "" && content.SenderKey != sess.SenderKey { - return sess, nil, 0, SenderKeyMismatch + return sess, nil, 0, ErrSenderKeyMismatch } plaintext, messageIndex, err := sess.Internal.Decrypt(content.MegolmCiphertext) if err != nil { - if errors.Is(err, olm.UnknownMessageIndex) && mach.RatchetKeysOnDecrypt { + if errors.Is(err, olm.ErrUnknownMessageIndex) && mach.RatchetKeysOnDecrypt { messageIndex, err = mach.checkUndecryptableMessageIndexDuplication(ctx, sess, evt, content) return sess, nil, messageIndex, fmt.Errorf("failed to decrypt megolm event: %w", err) } @@ -238,7 +249,7 @@ func (mach *OlmMachine) actuallyDecryptMegolmEvent(ctx context.Context, evt *eve } else if ok, err := mach.CryptoStore.ValidateMessageIndex(ctx, sess.SenderKey, content.SessionID, evt.ID, messageIndex, evt.Timestamp); err != nil { return sess, nil, messageIndex, fmt.Errorf("failed to check if message index is duplicate: %w", err) } else if !ok { - return sess, nil, messageIndex, fmt.Errorf("%w %d", DuplicateMessageIndex, messageIndex) + return sess, nil, messageIndex, fmt.Errorf("%w %d", ErrDuplicateMessageIndex, messageIndex) } // Normal clients don't care about tracking the ratchet state, so let them bypass the rest of the function @@ -290,24 +301,24 @@ func (mach *OlmMachine) actuallyDecryptMegolmEvent(ctx context.Context, evt *eve err = mach.CryptoStore.RedactGroupSession(ctx, sess.RoomID, sess.ID(), "maximum messages reached") if err != nil { log.Err(err).Msg("Failed to delete fully used session") - return sess, plaintext, messageIndex, RatchetError + return sess, plaintext, messageIndex, ErrRatchetError } else { log.Info().Msg("Deleted fully used session") } } else if ratchetCurrentIndex < ratchetTargetIndex && mach.RatchetKeysOnDecrypt { if err = sess.RatchetTo(ratchetTargetIndex); err != nil { log.Err(err).Msg("Failed to ratchet session") - return sess, plaintext, messageIndex, RatchetError + return sess, plaintext, messageIndex, ErrRatchetError } else if err = mach.CryptoStore.PutGroupSession(ctx, sess); err != nil { log.Err(err).Msg("Failed to store ratcheted session") - return sess, plaintext, messageIndex, RatchetError + return sess, plaintext, messageIndex, ErrRatchetError } else { log.Info().Msg("Ratcheted session forward") } } else if didModify { if err = mach.CryptoStore.PutGroupSession(ctx, sess); err != nil { log.Err(err).Msg("Failed to store updated ratchet safety data") - return sess, plaintext, messageIndex, RatchetError + return sess, plaintext, messageIndex, ErrRatchetError } else { log.Debug().Msg("Ratchet safety data changed (ratchet state didn't change)") } diff --git a/vendor/maunium.net/go/mautrix/crypto/decryptolm.go b/vendor/maunium.net/go/mautrix/crypto/decryptolm.go index 30cc4cf..cd02726 100644 --- a/vendor/maunium.net/go/mautrix/crypto/decryptolm.go +++ b/vendor/maunium.net/go/mautrix/crypto/decryptolm.go @@ -26,15 +26,27 @@ import ( ) var ( - UnsupportedAlgorithm = errors.New("unsupported event encryption algorithm") - NotEncryptedForMe = errors.New("olm event doesn't contain ciphertext for this device") - UnsupportedOlmMessageType = errors.New("unsupported olm message type") - DecryptionFailedWithMatchingSession = errors.New("decryption failed with matching session") - DecryptionFailedForNormalMessage = errors.New("decryption failed for normal message") - SenderMismatch = errors.New("mismatched sender in olm payload") - RecipientMismatch = errors.New("mismatched recipient in olm payload") - RecipientKeyMismatch = errors.New("mismatched recipient key in olm payload") - ErrDuplicateMessage = errors.New("duplicate olm message") + ErrUnsupportedAlgorithm = errors.New("unsupported event encryption algorithm") + ErrNotEncryptedForMe = errors.New("olm event doesn't contain ciphertext for this device") + ErrUnsupportedOlmMessageType = errors.New("unsupported olm message type") + ErrDecryptionFailedWithMatchingSession = errors.New("decryption failed with matching session") + ErrDecryptionFailedForNormalMessage = errors.New("decryption failed for normal message") + ErrSenderMismatch = errors.New("mismatched sender in olm payload") + ErrRecipientMismatch = errors.New("mismatched recipient in olm payload") + ErrRecipientKeyMismatch = errors.New("mismatched recipient key in olm payload") + ErrDuplicateMessage = errors.New("duplicate olm message") +) + +// Deprecated: use variables prefixed with Err +var ( + UnsupportedAlgorithm = ErrUnsupportedAlgorithm + NotEncryptedForMe = ErrNotEncryptedForMe + UnsupportedOlmMessageType = ErrUnsupportedOlmMessageType + DecryptionFailedWithMatchingSession = ErrDecryptionFailedWithMatchingSession + DecryptionFailedForNormalMessage = ErrDecryptionFailedForNormalMessage + SenderMismatch = ErrSenderMismatch + RecipientMismatch = ErrRecipientMismatch + RecipientKeyMismatch = ErrRecipientKeyMismatch ) // DecryptedOlmEvent represents an event that was decrypted from an event encrypted with the m.olm.v1.curve25519-aes-sha2 algorithm. @@ -56,13 +68,13 @@ type DecryptedOlmEvent struct { func (mach *OlmMachine) decryptOlmEvent(ctx context.Context, evt *event.Event) (*DecryptedOlmEvent, error) { content, ok := evt.Content.Parsed.(*event.EncryptedEventContent) if !ok { - return nil, IncorrectEncryptedContentType + return nil, ErrIncorrectEncryptedContentType } else if content.Algorithm != id.AlgorithmOlmV1 { - return nil, UnsupportedAlgorithm + return nil, ErrUnsupportedAlgorithm } ownContent, ok := content.OlmCiphertext[mach.account.IdentityKey()] if !ok { - return nil, NotEncryptedForMe + return nil, ErrNotEncryptedForMe } decrypted, err := mach.decryptAndParseOlmCiphertext(ctx, evt, content.SenderKey, ownContent.Type, ownContent.Body) if err != nil { @@ -78,7 +90,7 @@ type OlmEventKeys struct { func (mach *OlmMachine) decryptAndParseOlmCiphertext(ctx context.Context, evt *event.Event, senderKey id.SenderKey, olmType id.OlmMsgType, ciphertext string) (*DecryptedOlmEvent, error) { if olmType != id.OlmMsgTypePreKey && olmType != id.OlmMsgTypeMsg { - return nil, UnsupportedOlmMessageType + return nil, ErrUnsupportedOlmMessageType } log := mach.machOrContextLog(ctx).With(). @@ -102,11 +114,11 @@ func (mach *OlmMachine) decryptAndParseOlmCiphertext(ctx context.Context, evt *e } olmEvt.Type.Class = evt.Type.Class if evt.Sender != olmEvt.Sender { - return nil, SenderMismatch + return nil, ErrSenderMismatch } else if mach.Client.UserID != olmEvt.Recipient { - return nil, RecipientMismatch + return nil, ErrRecipientMismatch } else if mach.account.SigningKey() != olmEvt.RecipientKeys.Ed25519 { - return nil, RecipientKeyMismatch + return nil, ErrRecipientKeyMismatch } if len(olmEvt.Content.VeryRaw) > 0 { @@ -151,7 +163,7 @@ func (mach *OlmMachine) tryDecryptOlmCiphertext(ctx context.Context, sender id.U plaintext, err := mach.tryDecryptOlmCiphertextWithExistingSession(ctx, senderKey, olmType, ciphertext, ciphertextHash) if err != nil { - if err == DecryptionFailedWithMatchingSession { + if err == ErrDecryptionFailedWithMatchingSession { log.Warn().Msg("Found matching session, but decryption failed") go mach.unwedgeDevice(log, sender, senderKey) } @@ -169,10 +181,10 @@ func (mach *OlmMachine) tryDecryptOlmCiphertext(ctx context.Context, sender id.U // if it isn't one at this point in time anymore, so return early. if olmType != id.OlmMsgTypePreKey { go mach.unwedgeDevice(log, sender, senderKey) - return nil, DecryptionFailedForNormalMessage + return nil, ErrDecryptionFailedForNormalMessage } - accountBackup, err := mach.account.Internal.Pickle([]byte("tmp")) + accountBackup, _ := mach.account.Internal.Pickle([]byte("tmp")) log.Trace().Msg("Trying to create inbound session") endTimeTrace = mach.timeTrace(ctx, "creating inbound olm session", time.Second) session, err := mach.createInboundSession(ctx, senderKey, ciphertext) @@ -302,7 +314,7 @@ func (mach *OlmMachine) tryDecryptOlmCiphertextWithExistingSession( Str("session_description", session.Describe()). Msg("Failed to decrypt olm message") if olmType == id.OlmMsgTypePreKey { - return nil, DecryptionFailedWithMatchingSession + return nil, ErrDecryptionFailedWithMatchingSession } } else { endTimeTrace = mach.timeTrace(ctx, "updating session in database", time.Second) @@ -345,7 +357,7 @@ func (mach *OlmMachine) unwedgeDevice(log zerolog.Logger, sender id.UserID, send ctx := log.WithContext(mach.backgroundCtx) mach.recentlyUnwedgedLock.Lock() prevUnwedge, ok := mach.recentlyUnwedged[senderKey] - delta := time.Now().Sub(prevUnwedge) + delta := time.Since(prevUnwedge) if ok && delta < MinUnwedgeInterval { log.Debug(). Str("previous_recreation", delta.String()). diff --git a/vendor/maunium.net/go/mautrix/crypto/devicelist.go b/vendor/maunium.net/go/mautrix/crypto/devicelist.go index 61a2252..f0d2b12 100644 --- a/vendor/maunium.net/go/mautrix/crypto/devicelist.go +++ b/vendor/maunium.net/go/mautrix/crypto/devicelist.go @@ -22,14 +22,23 @@ import ( ) var ( - MismatchingDeviceID = errors.New("mismatching device ID in parameter and keys object") - MismatchingUserID = errors.New("mismatching user ID in parameter and keys object") - MismatchingSigningKey = errors.New("received update for device with different signing key") - NoSigningKeyFound = errors.New("didn't find ed25519 signing key") - NoIdentityKeyFound = errors.New("didn't find curve25519 identity key") - InvalidKeySignature = errors.New("invalid signature on device keys") + ErrMismatchingDeviceID = errors.New("mismatching device ID in parameter and keys object") + ErrMismatchingUserID = errors.New("mismatching user ID in parameter and keys object") + ErrMismatchingSigningKey = errors.New("received update for device with different signing key") + ErrNoSigningKeyFound = errors.New("didn't find ed25519 signing key") + ErrNoIdentityKeyFound = errors.New("didn't find curve25519 identity key") + ErrInvalidKeySignature = errors.New("invalid signature on device keys") + ErrUserNotTracked = errors.New("user is not tracked") +) - ErrUserNotTracked = errors.New("user is not tracked") +// Deprecated: use variables prefixed with Err +var ( + MismatchingDeviceID = ErrMismatchingDeviceID + MismatchingUserID = ErrMismatchingUserID + MismatchingSigningKey = ErrMismatchingSigningKey + NoSigningKeyFound = ErrNoSigningKeyFound + NoIdentityKeyFound = ErrNoIdentityKeyFound + InvalidKeySignature = ErrInvalidKeySignature ) func (mach *OlmMachine) LoadDevices(ctx context.Context, user id.UserID) (keys map[id.DeviceID]*id.Device) { @@ -312,28 +321,28 @@ func (mach *OlmMachine) OnDevicesChanged(ctx context.Context, userID id.UserID) func (mach *OlmMachine) validateDevice(userID id.UserID, deviceID id.DeviceID, deviceKeys mautrix.DeviceKeys, existing *id.Device) (*id.Device, error) { if deviceID != deviceKeys.DeviceID { - return nil, fmt.Errorf("%w (expected %s, got %s)", MismatchingDeviceID, deviceID, deviceKeys.DeviceID) + return nil, fmt.Errorf("%w (expected %s, got %s)", ErrMismatchingDeviceID, deviceID, deviceKeys.DeviceID) } else if userID != deviceKeys.UserID { - return nil, fmt.Errorf("%w (expected %s, got %s)", MismatchingUserID, userID, deviceKeys.UserID) + return nil, fmt.Errorf("%w (expected %s, got %s)", ErrMismatchingUserID, userID, deviceKeys.UserID) } signingKey := deviceKeys.Keys.GetEd25519(deviceID) identityKey := deviceKeys.Keys.GetCurve25519(deviceID) if signingKey == "" { - return nil, NoSigningKeyFound + return nil, ErrNoSigningKeyFound } else if identityKey == "" { - return nil, NoIdentityKeyFound + return nil, ErrNoIdentityKeyFound } if existing != nil && existing.SigningKey != signingKey { - return existing, fmt.Errorf("%w (expected %s, got %s)", MismatchingSigningKey, existing.SigningKey, signingKey) + return existing, fmt.Errorf("%w (expected %s, got %s)", ErrMismatchingSigningKey, existing.SigningKey, signingKey) } ok, err := signatures.VerifySignatureJSON(deviceKeys, userID, deviceID.String(), signingKey) if err != nil { return existing, fmt.Errorf("failed to verify signature: %w", err) } else if !ok { - return existing, InvalidKeySignature + return existing, ErrInvalidKeySignature } name, ok := deviceKeys.Unsigned["device_display_name"].(string) diff --git a/vendor/maunium.net/go/mautrix/crypto/encryptmegolm.go b/vendor/maunium.net/go/mautrix/crypto/encryptmegolm.go index b3d1961..ea97f76 100644 --- a/vendor/maunium.net/go/mautrix/crypto/encryptmegolm.go +++ b/vendor/maunium.net/go/mautrix/crypto/encryptmegolm.go @@ -25,7 +25,12 @@ import ( ) var ( - NoGroupSession = errors.New("no group session created") + ErrNoGroupSession = errors.New("no group session created") +) + +// Deprecated: use variables prefixed with Err +var ( + NoGroupSession = ErrNoGroupSession ) func getRawJSON[T any](content json.RawMessage, path ...string) *T { @@ -82,7 +87,7 @@ type rawMegolmEvent struct { // IsShareError returns true if the error is caused by the lack of an outgoing megolm session and can be solved with OlmMachine.ShareGroupSession func IsShareError(err error) bool { - return err == SessionExpired || err == SessionNotShared || err == NoGroupSession + return err == ErrSessionExpired || err == ErrSessionNotShared || err == ErrNoGroupSession } func ParseMegolmMessageIndex(ciphertext []byte) (uint, error) { @@ -120,7 +125,7 @@ func (mach *OlmMachine) EncryptMegolmEventWithStateKey(ctx context.Context, room if err != nil { return nil, fmt.Errorf("failed to get outbound group session: %w", err) } else if session == nil { - return nil, NoGroupSession + return nil, ErrNoGroupSession } plaintext, err := json.Marshal(&rawMegolmEvent{ RoomID: roomID, diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/account/account.go b/vendor/maunium.net/go/mautrix/crypto/goolm/account/account.go index 4da08a7..b48843a 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/account/account.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/account/account.go @@ -334,7 +334,7 @@ func (a *Account) UnpickleLibOlm(buf []byte) error { if err != nil { return err } else if pickledVersion != accountPickleVersionLibOLM && pickledVersion != 3 && pickledVersion != 2 { - return fmt.Errorf("unpickle account: %w (found version %d)", olm.ErrBadVersion, pickledVersion) + return fmt.Errorf("unpickle account: %w (found version %d)", olm.ErrUnknownOlmPickleVersion, pickledVersion) } else if err = a.IdKeys.Ed25519.UnpickleLibOlm(decoder); err != nil { // read the ed25519 key pair return err } else if err = a.IdKeys.Curve25519.UnpickleLibOlm(decoder); err != nil { // read curve25519 key pair diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go b/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go index 061a052..58ee26f 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go @@ -4,7 +4,8 @@ import ( "encoding/base64" ) -// Deprecated: base64.RawStdEncoding should be used directly +// These methods should only be used for raw byte operations, never with string conversion + func Decode(input []byte) ([]byte, error) { decoded := make([]byte, base64.RawStdEncoding.DecodedLen(len(input))) writtenBytes, err := base64.RawStdEncoding.Decode(decoded, input) @@ -14,7 +15,6 @@ func Decode(input []byte) ([]byte, error) { return decoded[:writtenBytes], nil } -// Deprecated: base64.RawStdEncoding should be used directly func Encode(input []byte) []byte { encoded := make([]byte, base64.RawStdEncoding.EncodedLen(len(input))) base64.RawStdEncoding.Encode(encoded, input) diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go index 308e472..f765391 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go @@ -50,7 +50,7 @@ func UnpickleAsJSON(object any, pickled, key []byte, pickleVersion byte) error { } } if decrypted[0] != pickleVersion { - return fmt.Errorf("unpickle: %w", olm.ErrWrongPickleVersion) + return fmt.Errorf("unpickle: %w", olm.ErrUnknownJSONPickleVersion) } err = json.Unmarshal(decrypted[1:], object) if err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/message/group_message.go b/vendor/maunium.net/go/mautrix/crypto/goolm/message/group_message.go index f3d2250..c83540c 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/message/group_message.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/message/group_message.go @@ -39,7 +39,7 @@ func (r *GroupMessage) Decode(input []byte) (err error) { return } if r.Version != protocolVersion { - return fmt.Errorf("GroupMessage.Decode: %w", olm.ErrWrongProtocolVersion) + return fmt.Errorf("GroupMessage.Decode: %w (got %d, expected %d)", olm.ErrWrongProtocolVersion, r.Version, protocolVersion) } for { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/message/message.go b/vendor/maunium.net/go/mautrix/crypto/goolm/message/message.go index 9ef9363..b161a2d 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/message/message.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/message/message.go @@ -43,7 +43,7 @@ func (r *Message) Decode(input []byte) (err error) { return } if r.Version != protocolVersion { - return fmt.Errorf("Message.Decode: %w", olm.ErrWrongProtocolVersion) + return fmt.Errorf("Message.Decode: %w (got %d, expected %d)", olm.ErrWrongProtocolVersion, r.Version, protocolVersion) } for { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/message/prekey_message.go b/vendor/maunium.net/go/mautrix/crypto/goolm/message/prekey_message.go index 760be4c..4e3d495 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/message/prekey_message.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/message/prekey_message.go @@ -48,7 +48,7 @@ func (r *PreKeyMessage) Decode(input []byte) (err error) { return } if r.Version != protocolVersion { - return fmt.Errorf("PreKeyMessage.Decode: %w", olm.ErrWrongProtocolVersion) + return fmt.Errorf("PreKeyMessage.Decode: %w (got %d, expected %d)", olm.ErrWrongProtocolVersion, r.Version, protocolVersion) } for { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_export.go b/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_export.go index 956868b..d58dbb2 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_export.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_export.go @@ -35,7 +35,7 @@ func (s *MegolmSessionExport) Decode(input []byte) error { return fmt.Errorf("decrypt: %w", olm.ErrBadInput) } if input[0] != sessionExportVersion { - return fmt.Errorf("decrypt: %w", olm.ErrBadVersion) + return fmt.Errorf("decrypt: %w", olm.ErrUnknownOlmPickleVersion) } s.Counter = binary.BigEndian.Uint32(input[1:5]) copy(s.RatchetData[:], input[5:133]) diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_sharing.go b/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_sharing.go index 1624094..d04ef15 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_sharing.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/message/session_sharing.go @@ -42,7 +42,7 @@ func (s *MegolmSessionSharing) VerifyAndDecode(input []byte) error { } s.PublicKey = publicKey if input[0] != sessionSharingVersion { - return fmt.Errorf("verify: %w", olm.ErrBadVersion) + return fmt.Errorf("verify: %w", olm.ErrUnknownOlmPickleVersion) } s.Counter = binary.BigEndian.Uint32(input[1:5]) copy(s.RatchetData[:], input[5:133]) diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/pk/decryption.go b/vendor/maunium.net/go/mautrix/crypto/goolm/pk/decryption.go index afb01f7..cdb20eb 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/pk/decryption.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/pk/decryption.go @@ -103,7 +103,7 @@ func (a *Decryption) UnpickleLibOlm(unpickled []byte) error { if pickledVersion == decryptionPickleVersionLibOlm { return a.KeyPair.UnpickleLibOlm(decoder) } else { - return fmt.Errorf("unpickle olmSession: %w (found %d, expected %d)", olm.ErrBadVersion, pickledVersion, decryptionPickleVersionLibOlm) + return fmt.Errorf("unpickle olmSession: %w (found %d, expected %d)", olm.ErrUnknownOlmPickleVersion, pickledVersion, decryptionPickleVersionLibOlm) } } diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/pk/encryption.go b/vendor/maunium.net/go/mautrix/crypto/goolm/pk/encryption.go index 23f67dd..2897d9b 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/pk/encryption.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/pk/encryption.go @@ -37,6 +37,9 @@ func (e Encryption) Encrypt(plaintext []byte, privateKey crypto.Curve25519Privat return nil, nil, err } cipher, err := aessha2.NewAESSHA2(sharedSecret, nil) + if err != nil { + return nil, nil, err + } ciphertext, err = cipher.Encrypt(plaintext) if err != nil { return nil, nil, err diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/ratchet/olm.go b/vendor/maunium.net/go/mautrix/crypto/goolm/ratchet/olm.go index 229c9bd..9901ada 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/ratchet/olm.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/ratchet/olm.go @@ -142,7 +142,7 @@ func (r *Ratchet) Decrypt(input []byte) ([]byte, error) { return nil, err } if message.Version != protocolVersion { - return nil, fmt.Errorf("decrypt: %w", olm.ErrWrongProtocolVersion) + return nil, fmt.Errorf("decrypt: %w (got %d, expected %d)", olm.ErrWrongProtocolVersion, message.Version, protocolVersion) } if !message.HasCounter || len(message.RatchetKey) == 0 || len(message.Ciphertext) == 0 { return nil, fmt.Errorf("decrypt: %w", olm.ErrBadMessageFormat) diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_inbound_session.go b/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_inbound_session.go index 80dd71c..7ccbd26 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_inbound_session.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_inbound_session.go @@ -99,7 +99,7 @@ func (o *MegolmInboundSession) getRatchet(messageIndex uint32) (*megolm.Ratchet, } if (messageIndex - o.InitialRatchet.Counter) >= uint32(1<<31) { // the counter is before our initial ratchet - we can't decode this - return nil, fmt.Errorf("decrypt: %w", olm.ErrRatchetNotAvailable) + return nil, fmt.Errorf("decrypt: %w", olm.ErrUnknownMessageIndex) } // otherwise, start from the initial ratchet. Take a copy so that we don't overwrite the initial ratchet copiedRatchet := o.InitialRatchet @@ -126,7 +126,7 @@ func (o *MegolmInboundSession) Decrypt(ciphertext []byte) ([]byte, uint, error) return nil, 0, err } if msg.Version != protocolVersion { - return nil, 0, fmt.Errorf("decrypt: %w", olm.ErrWrongProtocolVersion) + return nil, 0, fmt.Errorf("decrypt: %w (got %d, expected %d)", olm.ErrWrongProtocolVersion, msg.Version, protocolVersion) } if msg.Ciphertext == nil || !msg.HasMessageIndex { return nil, 0, fmt.Errorf("decrypt: %w", olm.ErrBadMessageFormat) @@ -206,7 +206,7 @@ func (o *MegolmInboundSession) UnpickleLibOlm(value []byte) error { return err } if pickledVersion != megolmInboundSessionPickleVersionLibOlm && pickledVersion != 1 { - return fmt.Errorf("unpickle MegolmInboundSession: %w (found version %d)", olm.ErrBadVersion, pickledVersion) + return fmt.Errorf("unpickle MegolmInboundSession: %w (found version %d)", olm.ErrUnknownOlmPickleVersion, pickledVersion) } if err = o.InitialRatchet.UnpickleLibOlm(decoder); err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_outbound_session.go b/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_outbound_session.go index 2b8e1c8..7f92353 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_outbound_session.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/session/megolm_outbound_session.go @@ -101,8 +101,10 @@ func (o *MegolmOutboundSession) Unpickle(pickled, key []byte) error { func (o *MegolmOutboundSession) UnpickleLibOlm(buf []byte) error { decoder := libolmpickle.NewDecoder(buf) pickledVersion, err := decoder.ReadUInt32() - if pickledVersion != megolmOutboundSessionPickleVersionLibOlm { - return fmt.Errorf("unpickle MegolmInboundSession: %w (found version %d)", olm.ErrBadVersion, pickledVersion) + if err != nil { + return fmt.Errorf("unpickle MegolmOutboundSession: failed to read version: %w", err) + } else if pickledVersion != megolmOutboundSessionPickleVersionLibOlm { + return fmt.Errorf("unpickle MegolmInboundSession: %w (found version %d)", olm.ErrUnknownOlmPickleVersion, pickledVersion) } if err = o.Ratchet.UnpickleLibOlm(decoder); err != nil { return err diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/session/olm_session.go b/vendor/maunium.net/go/mautrix/crypto/goolm/session/olm_session.go index b99ab63..a1cb8d6 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/session/olm_session.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/session/olm_session.go @@ -168,11 +168,11 @@ func NewInboundOlmSession(identityKeyAlice *crypto.Curve25519PublicKey, received msg := message.Message{} err = msg.Decode(oneTimeMsg.Message) if err != nil { - return nil, fmt.Errorf("Message decode: %w", err) + return nil, fmt.Errorf("message decode: %w", err) } if len(msg.RatchetKey) == 0 { - return nil, fmt.Errorf("Message missing ratchet key: %w", olm.ErrBadMessageFormat) + return nil, fmt.Errorf("message missing ratchet key: %w", olm.ErrBadMessageFormat) } //Init Ratchet s.Ratchet.InitializeAsBob(secret, msg.RatchetKey) @@ -203,7 +203,7 @@ func (s *OlmSession) ID() id.SessionID { copy(message[crypto.Curve25519PrivateKeyLength:], s.AliceBaseKey) copy(message[2*crypto.Curve25519PrivateKeyLength:], s.BobOneTimeKey) hash := sha256.Sum256(message) - res := id.SessionID(goolmbase64.Encode(hash[:])) + res := id.SessionID(base64.RawStdEncoding.EncodeToString(hash[:])) return res } @@ -325,7 +325,7 @@ func (s *OlmSession) Decrypt(crypttext string, msgType id.OlmMsgType) ([]byte, e if len(crypttext) == 0 { return nil, fmt.Errorf("decrypt: %w", olm.ErrEmptyInput) } - decodedCrypttext, err := goolmbase64.Decode([]byte(crypttext)) + decodedCrypttext, err := base64.RawStdEncoding.DecodeString(crypttext) if err != nil { return nil, err } @@ -365,6 +365,9 @@ func (o *OlmSession) Unpickle(pickled, key []byte) error { func (o *OlmSession) UnpickleLibOlm(buf []byte) error { decoder := libolmpickle.NewDecoder(buf) pickledVersion, err := decoder.ReadUInt32() + if err != nil { + return fmt.Errorf("unpickle olmSession: failed to read version: %w", err) + } var includesChainIndex bool switch pickledVersion { @@ -373,7 +376,7 @@ func (o *OlmSession) UnpickleLibOlm(buf []byte) error { case uint32(0x80000001): includesChainIndex = true default: - return fmt.Errorf("unpickle olmSession: %w (found version %d)", olm.ErrBadVersion, pickledVersion) + return fmt.Errorf("unpickle olmSession: %w (found version %d)", olm.ErrUnknownOlmPickleVersion, pickledVersion) } if o.ReceivedMessage, err = decoder.ReadBool(); err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/session/register.go b/vendor/maunium.net/go/mautrix/crypto/goolm/session/register.go index a88d12f..b95a44a 100644 --- a/vendor/maunium.net/go/mautrix/crypto/goolm/session/register.go +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/session/register.go @@ -14,7 +14,7 @@ func Register() { // Inbound Session olm.InitInboundGroupSessionFromPickled = func(pickled, key []byte) (olm.InboundGroupSession, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } if len(key) == 0 { key = []byte(" ") @@ -23,13 +23,13 @@ func Register() { } olm.InitNewInboundGroupSession = func(sessionKey []byte) (olm.InboundGroupSession, error) { if len(sessionKey) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } return NewMegolmInboundSession(sessionKey) } olm.InitInboundGroupSessionImport = func(sessionKey []byte) (olm.InboundGroupSession, error) { if len(sessionKey) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } return NewMegolmInboundSessionFromExport(sessionKey) } @@ -40,7 +40,7 @@ func Register() { // Outbound Session olm.InitNewOutboundGroupSessionFromPickled = func(pickled, key []byte) (olm.OutboundGroupSession, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } lenKey := len(key) if lenKey == 0 { diff --git a/vendor/maunium.net/go/mautrix/crypto/keybackup.go b/vendor/maunium.net/go/mautrix/crypto/keybackup.go index d8b3d71..ceec1d5 100644 --- a/vendor/maunium.net/go/mautrix/crypto/keybackup.go +++ b/vendor/maunium.net/go/mautrix/crypto/keybackup.go @@ -56,11 +56,12 @@ func (mach *OlmMachine) GetAndVerifyLatestKeyBackupVersion(ctx context.Context, // ...by deriving the public key from a private key that it obtained from a trusted source. Trusted sources for the private // key include the user entering the key, retrieving the key stored in secret storage, or obtaining the key via secret sharing // from a verified device belonging to the same user." - megolmBackupDerivedPublicKey := id.Ed25519(base64.RawStdEncoding.EncodeToString(megolmBackupKey.PublicKey().Bytes())) - if megolmBackupKey != nil && versionInfo.AuthData.PublicKey == megolmBackupDerivedPublicKey { - log.Debug().Msg("key backup is trusted based on derived public key") - return versionInfo, nil - } else { + if megolmBackupKey != nil { + megolmBackupDerivedPublicKey := id.Ed25519(base64.RawStdEncoding.EncodeToString(megolmBackupKey.PublicKey().Bytes())) + if versionInfo.AuthData.PublicKey == megolmBackupDerivedPublicKey { + log.Debug().Msg("Key backup is trusted based on derived public key") + return versionInfo, nil + } log.Debug(). Stringer("expected_key", megolmBackupDerivedPublicKey). Stringer("actual_key", versionInfo.AuthData.PublicKey). diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/account.go b/vendor/maunium.net/go/mautrix/crypto/libolm/account.go index f6f916e..0350f08 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/account.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/account.go @@ -33,7 +33,7 @@ var _ olm.Account = (*Account)(nil) // "INVALID_BASE64". func AccountFromPickled(pickled, key []byte) (*Account, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } a := NewBlankAccount() return a, a.Unpickle(pickled, key) @@ -53,7 +53,7 @@ func NewAccount() (*Account, error) { random := make([]byte, a.createRandomLen()+1) _, err := rand.Read(random) if err != nil { - panic(olm.NotEnoughGoRandom) + panic(olm.ErrNotEnoughGoRandom) } ret := C.olm_create_account( (*C.OlmAccount)(a.int), @@ -128,7 +128,7 @@ func (a *Account) genOneTimeKeysRandomLen(num uint) uint { // supplied key. func (a *Account) Pickle(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, olm.NoKeyProvided + return nil, olm.ErrNoKeyProvided } pickled := make([]byte, a.pickleLen()) r := C.olm_pickle_account( @@ -145,7 +145,7 @@ func (a *Account) Pickle(key []byte) ([]byte, error) { func (a *Account) Unpickle(pickled, key []byte) error { if len(key) == 0 { - return olm.NoKeyProvided + return olm.ErrNoKeyProvided } r := C.olm_unpickle_account( (*C.OlmAccount)(a.int), @@ -198,7 +198,7 @@ func (a *Account) MarshalJSON() ([]byte, error) { // Deprecated func (a *Account) UnmarshalJSON(data []byte) error { if len(data) == 0 || data[0] != '"' || data[len(data)-1] != '"' { - return olm.InputNotJSONString + return olm.ErrInputNotJSONString } if a.int == nil { *a = *NewBlankAccount() @@ -235,7 +235,7 @@ func (a *Account) IdentityKeys() (id.Ed25519, id.Curve25519, error) { // Account. func (a *Account) Sign(message []byte) ([]byte, error) { if len(message) == 0 { - panic(olm.EmptyInput) + panic(olm.ErrEmptyInput) } signature := make([]byte, a.signatureLen()) r := C.olm_account_sign( @@ -299,7 +299,7 @@ func (a *Account) GenOneTimeKeys(num uint) error { random := make([]byte, a.genOneTimeKeysRandomLen(num)+1) _, err := rand.Read(random) if err != nil { - return olm.NotEnoughGoRandom + return olm.ErrNotEnoughGoRandom } r := C.olm_account_generate_one_time_keys( (*C.OlmAccount)(a.int), @@ -319,13 +319,13 @@ func (a *Account) GenOneTimeKeys(num uint) error { // keys couldn't be decoded as base64 then the error will be "INVALID_BASE64" func (a *Account) NewOutboundSession(theirIdentityKey, theirOneTimeKey id.Curve25519) (olm.Session, error) { if len(theirIdentityKey) == 0 || len(theirOneTimeKey) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankSession() random := make([]byte, s.createOutboundRandomLen()+1) _, err := rand.Read(random) if err != nil { - panic(olm.NotEnoughGoRandom) + panic(olm.ErrNotEnoughGoRandom) } theirIdentityKeyCopy := []byte(theirIdentityKey) theirOneTimeKeyCopy := []byte(theirOneTimeKey) @@ -357,7 +357,7 @@ func (a *Account) NewOutboundSession(theirIdentityKey, theirOneTimeKey id.Curve2 // time key then the error will be "BAD_MESSAGE_KEY_ID". func (a *Account) NewInboundSession(oneTimeKeyMsg string) (olm.Session, error) { if len(oneTimeKeyMsg) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankSession() oneTimeKeyMsgCopy := []byte(oneTimeKeyMsg) @@ -383,7 +383,7 @@ func (a *Account) NewInboundSession(oneTimeKeyMsg string) (olm.Session, error) { // time key then the error will be "BAD_MESSAGE_KEY_ID". func (a *Account) NewInboundSessionFrom(theirIdentityKey *id.Curve25519, oneTimeKeyMsg string) (olm.Session, error) { if theirIdentityKey == nil || len(oneTimeKeyMsg) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } theirIdentityKeyCopy := []byte(*theirIdentityKey) oneTimeKeyMsgCopy := []byte(oneTimeKeyMsg) diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/error.go b/vendor/maunium.net/go/mautrix/crypto/libolm/error.go index 9ca415e..6fb5512 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/error.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/error.go @@ -11,21 +11,21 @@ import ( ) var errorMap = map[string]error{ - "NOT_ENOUGH_RANDOM": olm.NotEnoughRandom, - "OUTPUT_BUFFER_TOO_SMALL": olm.OutputBufferTooSmall, - "BAD_MESSAGE_VERSION": olm.BadMessageVersion, - "BAD_MESSAGE_FORMAT": olm.BadMessageFormat, - "BAD_MESSAGE_MAC": olm.BadMessageMAC, - "BAD_MESSAGE_KEY_ID": olm.BadMessageKeyID, - "INVALID_BASE64": olm.InvalidBase64, - "BAD_ACCOUNT_KEY": olm.BadAccountKey, - "UNKNOWN_PICKLE_VERSION": olm.UnknownPickleVersion, - "CORRUPTED_PICKLE": olm.CorruptedPickle, - "BAD_SESSION_KEY": olm.BadSessionKey, - "UNKNOWN_MESSAGE_INDEX": olm.UnknownMessageIndex, - "BAD_LEGACY_ACCOUNT_PICKLE": olm.BadLegacyAccountPickle, - "BAD_SIGNATURE": olm.BadSignature, - "INPUT_BUFFER_TOO_SMALL": olm.InputBufferTooSmall, + "NOT_ENOUGH_RANDOM": olm.ErrLibolmNotEnoughRandom, + "OUTPUT_BUFFER_TOO_SMALL": olm.ErrLibolmOutputBufferTooSmall, + "BAD_MESSAGE_VERSION": olm.ErrWrongProtocolVersion, + "BAD_MESSAGE_FORMAT": olm.ErrBadMessageFormat, + "BAD_MESSAGE_MAC": olm.ErrBadMAC, + "BAD_MESSAGE_KEY_ID": olm.ErrBadMessageKeyID, + "INVALID_BASE64": olm.ErrLibolmInvalidBase64, + "BAD_ACCOUNT_KEY": olm.ErrLibolmBadAccountKey, + "UNKNOWN_PICKLE_VERSION": olm.ErrUnknownOlmPickleVersion, + "CORRUPTED_PICKLE": olm.ErrLibolmCorruptedPickle, + "BAD_SESSION_KEY": olm.ErrLibolmBadSessionKey, + "UNKNOWN_MESSAGE_INDEX": olm.ErrUnknownMessageIndex, + "BAD_LEGACY_ACCOUNT_PICKLE": olm.ErrLibolmBadLegacyAccountPickle, + "BAD_SIGNATURE": olm.ErrBadSignature, + "INPUT_BUFFER_TOO_SMALL": olm.ErrInputToSmall, } func convertError(errCode string) error { diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/inboundgroupsession.go b/vendor/maunium.net/go/mautrix/crypto/libolm/inboundgroupsession.go index 5606475..8815ac3 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/inboundgroupsession.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/inboundgroupsession.go @@ -31,7 +31,7 @@ var _ olm.InboundGroupSession = (*InboundGroupSession)(nil) // base64 couldn't be decoded then the error will be "INVALID_BASE64". func InboundGroupSessionFromPickled(pickled, key []byte) (*InboundGroupSession, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } lenKey := len(key) if lenKey == 0 { @@ -48,7 +48,7 @@ func InboundGroupSessionFromPickled(pickled, key []byte) (*InboundGroupSession, // "OLM_BAD_SESSION_KEY". func NewInboundGroupSession(sessionKey []byte) (*InboundGroupSession, error) { if len(sessionKey) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankInboundGroupSession() r := C.olm_init_inbound_group_session( @@ -69,7 +69,7 @@ func NewInboundGroupSession(sessionKey []byte) (*InboundGroupSession, error) { // error will be "OLM_BAD_SESSION_KEY". func InboundGroupSessionImport(sessionKey []byte) (*InboundGroupSession, error) { if len(sessionKey) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankInboundGroupSession() r := C.olm_import_inbound_group_session( @@ -124,7 +124,7 @@ func (s *InboundGroupSession) pickleLen() uint { // InboundGroupSession using the supplied key. func (s *InboundGroupSession) Pickle(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, olm.NoKeyProvided + return nil, olm.ErrNoKeyProvided } pickled := make([]byte, s.pickleLen()) r := C.olm_pickle_inbound_group_session( @@ -143,9 +143,9 @@ func (s *InboundGroupSession) Pickle(key []byte) ([]byte, error) { func (s *InboundGroupSession) Unpickle(pickled, key []byte) error { if len(key) == 0 { - return olm.NoKeyProvided + return olm.ErrNoKeyProvided } else if len(pickled) == 0 { - return olm.EmptyInput + return olm.ErrEmptyInput } r := C.olm_unpickle_inbound_group_session( (*C.OlmInboundGroupSession)(s.int), @@ -200,7 +200,7 @@ func (s *InboundGroupSession) MarshalJSON() ([]byte, error) { // Deprecated func (s *InboundGroupSession) UnmarshalJSON(data []byte) error { if len(data) == 0 || data[0] != '"' || data[len(data)-1] != '"' { - return olm.InputNotJSONString + return olm.ErrInputNotJSONString } if s == nil || s.int == nil { *s = *NewBlankInboundGroupSession() @@ -217,7 +217,7 @@ func (s *InboundGroupSession) UnmarshalJSON(data []byte) error { // will be "BAD_MESSAGE_FORMAT". func (s *InboundGroupSession) decryptMaxPlaintextLen(message []byte) (uint, error) { if len(message) == 0 { - return 0, olm.EmptyInput + return 0, olm.ErrEmptyInput } // olm_group_decrypt_max_plaintext_length destroys the input, so we have to clone it messageCopy := bytes.Clone(message) @@ -244,7 +244,7 @@ func (s *InboundGroupSession) decryptMaxPlaintextLen(message []byte) (uint, erro // was shared with us) the error will be "OLM_UNKNOWN_MESSAGE_INDEX". func (s *InboundGroupSession) Decrypt(message []byte) ([]byte, uint, error) { if len(message) == 0 { - return nil, 0, olm.EmptyInput + return nil, 0, olm.ErrEmptyInput } decryptMaxPlaintextLen, err := s.decryptMaxPlaintextLen(message) if err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/outboundgroupsession.go b/vendor/maunium.net/go/mautrix/crypto/libolm/outboundgroupsession.go index 646929e..ca5b68f 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/outboundgroupsession.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/outboundgroupsession.go @@ -84,7 +84,7 @@ func (s *OutboundGroupSession) pickleLen() uint { // OutboundGroupSession using the supplied key. func (s *OutboundGroupSession) Pickle(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, olm.NoKeyProvided + return nil, olm.ErrNoKeyProvided } pickled := make([]byte, s.pickleLen()) r := C.olm_pickle_outbound_group_session( @@ -103,7 +103,7 @@ func (s *OutboundGroupSession) Pickle(key []byte) ([]byte, error) { func (s *OutboundGroupSession) Unpickle(pickled, key []byte) error { if len(key) == 0 { - return olm.NoKeyProvided + return olm.ErrNoKeyProvided } r := C.olm_unpickle_outbound_group_session( (*C.OlmOutboundGroupSession)(s.int), @@ -159,7 +159,7 @@ func (s *OutboundGroupSession) MarshalJSON() ([]byte, error) { // Deprecated func (s *OutboundGroupSession) UnmarshalJSON(data []byte) error { if len(data) == 0 || data[0] != '"' || data[len(data)-1] != '"' { - return olm.InputNotJSONString + return olm.ErrInputNotJSONString } if s == nil || s.int == nil { *s = *NewBlankOutboundGroupSession() @@ -183,7 +183,7 @@ func (s *OutboundGroupSession) encryptMsgLen(plainTextLen int) uint { // as base64. func (s *OutboundGroupSession) Encrypt(plaintext []byte) ([]byte, error) { if len(plaintext) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } message := make([]byte, s.encryptMsgLen(len(plaintext))) r := C.olm_group_encrypt( diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/pk.go b/vendor/maunium.net/go/mautrix/crypto/libolm/pk.go index 3553214..2683cf1 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/pk.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/pk.go @@ -86,7 +86,7 @@ func NewPKSigning() (*PKSigning, error) { seed := make([]byte, pkSigningSeedLength()) _, err := rand.Read(seed) if err != nil { - panic(olm.NotEnoughGoRandom) + panic(olm.ErrNotEnoughGoRandom) } pk, err := NewPKSigningFromSeed(seed) return pk, err diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/register.go b/vendor/maunium.net/go/mautrix/crypto/libolm/register.go index f091d82..ddf8461 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/register.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/register.go @@ -65,7 +65,7 @@ func Register() { olm.InitNewOutboundGroupSessionFromPickled = func(pickled, key []byte) (olm.OutboundGroupSession, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankOutboundGroupSession() return s, s.Unpickle(pickled, key) diff --git a/vendor/maunium.net/go/mautrix/crypto/libolm/session.go b/vendor/maunium.net/go/mautrix/crypto/libolm/session.go index 57e631c..1441df2 100644 --- a/vendor/maunium.net/go/mautrix/crypto/libolm/session.go +++ b/vendor/maunium.net/go/mautrix/crypto/libolm/session.go @@ -51,7 +51,7 @@ func sessionSize() uint { // "INVALID_BASE64". func SessionFromPickled(pickled, key []byte) (*Session, error) { if len(pickled) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } s := NewBlankSession() return s, s.Unpickle(pickled, key) @@ -118,7 +118,7 @@ func (s *Session) encryptMsgLen(plainTextLen int) uint { // will be "BAD_MESSAGE_FORMAT". func (s *Session) decryptMaxPlaintextLen(message string, msgType id.OlmMsgType) (uint, error) { if len(message) == 0 { - return 0, olm.EmptyInput + return 0, olm.ErrEmptyInput } messageCopy := []byte(message) r := C.olm_decrypt_max_plaintext_length( @@ -138,7 +138,7 @@ func (s *Session) decryptMaxPlaintextLen(message string, msgType id.OlmMsgType) // supplied key. func (s *Session) Pickle(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, olm.NoKeyProvided + return nil, olm.ErrNoKeyProvided } pickled := make([]byte, s.pickleLen()) r := C.olm_pickle_session( @@ -158,7 +158,7 @@ func (s *Session) Pickle(key []byte) ([]byte, error) { // provided key. This function mutates the input pickled data slice. func (s *Session) Unpickle(pickled, key []byte) error { if len(key) == 0 { - return olm.NoKeyProvided + return olm.ErrNoKeyProvided } r := C.olm_unpickle_session( (*C.OlmSession)(s.int), @@ -213,7 +213,7 @@ func (s *Session) MarshalJSON() ([]byte, error) { // Deprecated func (s *Session) UnmarshalJSON(data []byte) error { if len(data) == 0 || data[0] != '"' || data[len(data)-1] != '"' { - return olm.InputNotJSONString + return olm.ErrInputNotJSONString } if s == nil || s.int == nil { *s = *NewBlankSession() @@ -256,7 +256,7 @@ func (s *Session) HasReceivedMessage() bool { // decoded then then the error will be "BAD_MESSAGE_FORMAT". func (s *Session) MatchesInboundSession(oneTimeKeyMsg string) (bool, error) { if len(oneTimeKeyMsg) == 0 { - return false, olm.EmptyInput + return false, olm.ErrEmptyInput } oneTimeKeyMsgCopy := []byte(oneTimeKeyMsg) r := C.olm_matches_inbound_session( @@ -284,7 +284,7 @@ func (s *Session) MatchesInboundSession(oneTimeKeyMsg string) (bool, error) { // decoded then then the error will be "BAD_MESSAGE_FORMAT". func (s *Session) MatchesInboundSessionFrom(theirIdentityKey, oneTimeKeyMsg string) (bool, error) { if len(theirIdentityKey) == 0 || len(oneTimeKeyMsg) == 0 { - return false, olm.EmptyInput + return false, olm.ErrEmptyInput } theirIdentityKeyCopy := []byte(theirIdentityKey) oneTimeKeyMsgCopy := []byte(oneTimeKeyMsg) @@ -325,14 +325,14 @@ func (s *Session) EncryptMsgType() id.OlmMsgType { // as base64. func (s *Session) Encrypt(plaintext []byte) (id.OlmMsgType, []byte, error) { if len(plaintext) == 0 { - return 0, nil, olm.EmptyInput + return 0, nil, olm.ErrEmptyInput } // Make the slice be at least length 1 random := make([]byte, s.encryptRandomLen()+1) _, err := rand.Read(random) if err != nil { // TODO can we just return err here? - return 0, nil, olm.NotEnoughGoRandom + return 0, nil, olm.ErrNotEnoughGoRandom } messageType := s.EncryptMsgType() message := make([]byte, s.encryptMsgLen(len(plaintext))) @@ -362,7 +362,7 @@ func (s *Session) Encrypt(plaintext []byte) (id.OlmMsgType, []byte, error) { // "BAD_MESSAGE_MAC". func (s *Session) Decrypt(message string, msgType id.OlmMsgType) ([]byte, error) { if len(message) == 0 { - return nil, olm.EmptyInput + return nil, olm.ErrEmptyInput } decryptMaxPlaintextLen, err := s.decryptMaxPlaintextLen(message, msgType) if err != nil { diff --git a/vendor/maunium.net/go/mautrix/crypto/machine.go b/vendor/maunium.net/go/mautrix/crypto/machine.go index 4d2e388..f8ebe90 100644 --- a/vendor/maunium.net/go/mautrix/crypto/machine.go +++ b/vendor/maunium.net/go/mautrix/crypto/machine.go @@ -205,7 +205,7 @@ func (mach *OlmMachine) FlushStore(ctx context.Context) error { func (mach *OlmMachine) timeTrace(ctx context.Context, thing string, expectedDuration time.Duration) func() { start := time.Now() return func() { - duration := time.Now().Sub(start) + duration := time.Since(start) if duration > expectedDuration { zerolog.Ctx(ctx).Warn(). Str("action", thing). diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/errors.go b/vendor/maunium.net/go/mautrix/crypto/olm/errors.go index 957d792..9e522b2 100644 --- a/vendor/maunium.net/go/mautrix/crypto/olm/errors.go +++ b/vendor/maunium.net/go/mautrix/crypto/olm/errors.go @@ -10,50 +10,67 @@ import "errors" // Those are the most common used errors var ( - ErrBadSignature = errors.New("bad signature") - ErrBadMAC = errors.New("bad mac") - ErrBadMessageFormat = errors.New("bad message format") - ErrBadVerification = errors.New("bad verification") - ErrWrongProtocolVersion = errors.New("wrong protocol version") - ErrEmptyInput = errors.New("empty input") - ErrNoKeyProvided = errors.New("no key") - ErrBadMessageKeyID = errors.New("bad message key id") - ErrRatchetNotAvailable = errors.New("ratchet not available: attempt to decode a message whose index is earlier than our earliest known session key") - ErrMsgIndexTooHigh = errors.New("message index too high") - ErrProtocolViolation = errors.New("not protocol message order") - ErrMessageKeyNotFound = errors.New("message key not found") - ErrChainTooHigh = errors.New("chain index too high") - ErrBadInput = errors.New("bad input") - ErrBadVersion = errors.New("wrong version") - ErrWrongPickleVersion = errors.New("wrong pickle version") - ErrInputToSmall = errors.New("input too small (truncated?)") - ErrOverflow = errors.New("overflow") + ErrBadSignature = errors.New("bad signature") + ErrBadMAC = errors.New("the message couldn't be decrypted (bad mac)") + ErrBadMessageFormat = errors.New("the message couldn't be decoded") + ErrBadVerification = errors.New("bad verification") + ErrWrongProtocolVersion = errors.New("wrong protocol version") + ErrEmptyInput = errors.New("empty input") + ErrNoKeyProvided = errors.New("no key provided") + ErrBadMessageKeyID = errors.New("the message references an unknown key ID") + ErrUnknownMessageIndex = errors.New("attempt to decode a message whose index is earlier than our earliest known session key") + ErrMsgIndexTooHigh = errors.New("message index too high") + ErrProtocolViolation = errors.New("not protocol message order") + ErrMessageKeyNotFound = errors.New("message key not found") + ErrChainTooHigh = errors.New("chain index too high") + ErrBadInput = errors.New("bad input") + ErrUnknownOlmPickleVersion = errors.New("unknown olm pickle version") + ErrUnknownJSONPickleVersion = errors.New("unknown JSON pickle version") + ErrInputToSmall = errors.New("input too small (truncated?)") ) // Error codes from go-olm var ( - EmptyInput = errors.New("empty input") - NoKeyProvided = errors.New("no pickle key provided") - NotEnoughGoRandom = errors.New("couldn't get enough randomness from crypto/rand") - SignatureNotFound = errors.New("input JSON doesn't contain signature from specified device") - InputNotJSONString = errors.New("input doesn't look like a JSON string") + ErrNotEnoughGoRandom = errors.New("couldn't get enough randomness from crypto/rand") + ErrInputNotJSONString = errors.New("input doesn't look like a JSON string") ) // Error codes from olm code var ( - NotEnoughRandom = errors.New("not enough entropy was supplied") - OutputBufferTooSmall = errors.New("supplied output buffer is too small") - BadMessageVersion = errors.New("the message version is unsupported") - BadMessageFormat = errors.New("the message couldn't be decoded") - BadMessageMAC = errors.New("the message couldn't be decrypted") - BadMessageKeyID = errors.New("the message references an unknown key ID") - InvalidBase64 = errors.New("the input base64 was invalid") - BadAccountKey = errors.New("the supplied account key is invalid") - UnknownPickleVersion = errors.New("the pickled object is too new") - CorruptedPickle = errors.New("the pickled object couldn't be decoded") - BadSessionKey = errors.New("attempt to initialise an inbound group session from an invalid session key") - UnknownMessageIndex = errors.New("attempt to decode a message whose index is earlier than our earliest known session key") - BadLegacyAccountPickle = errors.New("attempt to unpickle an account which uses pickle version 1") - BadSignature = errors.New("received message had a bad signature") - InputBufferTooSmall = errors.New("the input data was too small to be valid") + ErrLibolmInvalidBase64 = errors.New("the input base64 was invalid") + + ErrLibolmNotEnoughRandom = errors.New("not enough entropy was supplied") + ErrLibolmOutputBufferTooSmall = errors.New("supplied output buffer is too small") + ErrLibolmBadAccountKey = errors.New("the supplied account key is invalid") + ErrLibolmCorruptedPickle = errors.New("the pickled object couldn't be decoded") + ErrLibolmBadSessionKey = errors.New("attempt to initialise an inbound group session from an invalid session key") + ErrLibolmBadLegacyAccountPickle = errors.New("attempt to unpickle an account which uses pickle version 1") +) + +// Deprecated: use variables prefixed with Err +var ( + EmptyInput = ErrEmptyInput + BadSignature = ErrBadSignature + InvalidBase64 = ErrLibolmInvalidBase64 + BadMessageKeyID = ErrBadMessageKeyID + BadMessageFormat = ErrBadMessageFormat + BadMessageVersion = ErrWrongProtocolVersion + BadMessageMAC = ErrBadMAC + UnknownPickleVersion = ErrUnknownOlmPickleVersion + NotEnoughRandom = ErrLibolmNotEnoughRandom + OutputBufferTooSmall = ErrLibolmOutputBufferTooSmall + BadAccountKey = ErrLibolmBadAccountKey + CorruptedPickle = ErrLibolmCorruptedPickle + BadSessionKey = ErrLibolmBadSessionKey + UnknownMessageIndex = ErrUnknownMessageIndex + BadLegacyAccountPickle = ErrLibolmBadLegacyAccountPickle + InputBufferTooSmall = ErrInputToSmall + NoKeyProvided = ErrNoKeyProvided + + NotEnoughGoRandom = ErrNotEnoughGoRandom + InputNotJSONString = ErrInputNotJSONString + + ErrBadVersion = ErrUnknownJSONPickleVersion + ErrWrongPickleVersion = ErrUnknownJSONPickleVersion + ErrRatchetNotAvailable = ErrUnknownMessageIndex ) diff --git a/vendor/maunium.net/go/mautrix/crypto/sessions.go b/vendor/maunium.net/go/mautrix/crypto/sessions.go index aecb041..6b90c99 100644 --- a/vendor/maunium.net/go/mautrix/crypto/sessions.go +++ b/vendor/maunium.net/go/mautrix/crypto/sessions.go @@ -18,8 +18,14 @@ import ( ) var ( - SessionNotShared = errors.New("session has not been shared") - SessionExpired = errors.New("session has expired") + ErrSessionNotShared = errors.New("session has not been shared") + ErrSessionExpired = errors.New("session has expired") +) + +// Deprecated: use variables prefixed with Err +var ( + SessionNotShared = ErrSessionNotShared + SessionExpired = ErrSessionExpired ) // OlmSessionList is a list of OlmSessions. @@ -255,9 +261,9 @@ func (ogs *OutboundGroupSession) Expired() bool { func (ogs *OutboundGroupSession) Encrypt(plaintext []byte) ([]byte, error) { if !ogs.Shared { - return nil, SessionNotShared + return nil, ErrSessionNotShared } else if ogs.Expired() { - return nil, SessionExpired + return nil, ErrSessionExpired } ogs.MessageCount++ ogs.LastEncryptedTime = time.Now() diff --git a/vendor/maunium.net/go/mautrix/crypto/ssss/client.go b/vendor/maunium.net/go/mautrix/crypto/ssss/client.go index e30925d..8691d03 100644 --- a/vendor/maunium.net/go/mautrix/crypto/ssss/client.go +++ b/vendor/maunium.net/go/mautrix/crypto/ssss/client.go @@ -95,6 +95,22 @@ func (mach *Machine) SetEncryptedAccountData(ctx context.Context, eventType even return mach.Client.SetAccountData(ctx, eventType.Type, &EncryptedAccountDataEventContent{Encrypted: encrypted}) } +// SetEncryptedAccountDataWithMetadata encrypts the given data with the given keys and stores it, +// alongside the unencrypted metadata, on the server. +func (mach *Machine) SetEncryptedAccountDataWithMetadata(ctx context.Context, eventType event.Type, data []byte, metadata map[string]any, keys ...*Key) error { + if len(keys) == 0 { + return ErrNoKeyGiven + } + encrypted := make(map[string]EncryptedKeyData, len(keys)) + for _, key := range keys { + encrypted[key.ID] = key.Encrypt(eventType.Type, data) + } + return mach.Client.SetAccountData(ctx, eventType.Type, &EncryptedAccountDataEventContent{ + Encrypted: encrypted, + Metadata: metadata, + }) +} + // GenerateAndUploadKey generates a new SSSS key and stores the metadata on the server. func (mach *Machine) GenerateAndUploadKey(ctx context.Context, passphrase string) (key *Key, err error) { key, err = NewKey(passphrase) diff --git a/vendor/maunium.net/go/mautrix/crypto/ssss/types.go b/vendor/maunium.net/go/mautrix/crypto/ssss/types.go index 345393b..c08f107 100644 --- a/vendor/maunium.net/go/mautrix/crypto/ssss/types.go +++ b/vendor/maunium.net/go/mautrix/crypto/ssss/types.go @@ -57,6 +57,7 @@ type EncryptedKeyData struct { type EncryptedAccountDataEventContent struct { Encrypted map[string]EncryptedKeyData `json:"encrypted"` + Metadata map[string]any `json:"com.beeper.metadata,omitzero"` } func (ed *EncryptedAccountDataEventContent) Decrypt(eventType string, key *Key) ([]byte, error) { diff --git a/vendor/maunium.net/go/mautrix/error.go b/vendor/maunium.net/go/mautrix/error.go index 826af17..5ff671e 100644 --- a/vendor/maunium.net/go/mautrix/error.go +++ b/vendor/maunium.net/go/mautrix/error.go @@ -85,6 +85,10 @@ var ( ErrResponseTooLong = errors.New("response content length too long") ErrBodyReadReachedLimit = errors.New("reached response size limit while reading body") + + // Special error that indicates we should retry canceled contexts. Note that on it's own this + // is useless, the context itself must also be replaced. + ErrContextCancelRetry = errors.New("retry canceled context") ) // HTTPError An HTTP Error response, which may wrap an underlying native Go Error. diff --git a/vendor/maunium.net/go/mautrix/event/beeper.go b/vendor/maunium.net/go/mautrix/event/beeper.go index 95b4a57..75c18aa 100644 --- a/vendor/maunium.net/go/mautrix/event/beeper.go +++ b/vendor/maunium.net/go/mautrix/event/beeper.go @@ -53,6 +53,8 @@ type BeeperMessageStatusEventContent struct { LastRetry id.EventID `json:"last_retry,omitempty"` + TargetTxnID string `json:"relates_to_txn_id,omitempty"` + MutateEventKey string `json:"mutate_event_key,omitempty"` // Indicates the set of users to whom the event was delivered. If nil, then @@ -87,7 +89,17 @@ type BeeperRoomKeyAckEventContent struct { } type BeeperChatDeleteEventContent struct { - DeleteForEveryone bool `json:"delete_for_everyone,omitempty"` + DeleteForEveryone bool `json:"delete_for_everyone,omitempty"` + FromMessageRequest bool `json:"from_message_request,omitempty"` +} + +type BeeperAcceptMessageRequestEventContent struct { +} + +type BeeperSendStateEventContent struct { + Type string `json:"type"` + StateKey string `json:"state_key"` + Content Content `json:"content"` } type IntOrString int diff --git a/vendor/maunium.net/go/mautrix/event/capabilities.d.ts b/vendor/maunium.net/go/mautrix/event/capabilities.d.ts index 1fbc961..26aeb34 100644 --- a/vendor/maunium.net/go/mautrix/event/capabilities.d.ts +++ b/vendor/maunium.net/go/mautrix/event/capabilities.d.ts @@ -77,6 +77,11 @@ export interface RoomFeatures { delete_chat?: boolean /** Whether deleting the chat for all participants is supported. */ delete_chat_for_everyone?: boolean + /** What can be done with message requests? */ + message_request?: { + accept_with_message?: CapabilitySupportLevel + accept_with_button?: CapabilitySupportLevel + } } declare type integer = number diff --git a/vendor/maunium.net/go/mautrix/event/capabilities.go b/vendor/maunium.net/go/mautrix/event/capabilities.go index 4b7ff18..a86c726 100644 --- a/vendor/maunium.net/go/mautrix/event/capabilities.go +++ b/vendor/maunium.net/go/mautrix/event/capabilities.go @@ -61,6 +61,8 @@ type RoomFeatures struct { DeleteChat bool `json:"delete_chat,omitempty"` DeleteChatForEveryone bool `json:"delete_chat_for_everyone,omitempty"` + MessageRequest *MessageRequestFeatures `json:"message_request,omitempty"` + PerMessageProfileRelay bool `json:"-"` } @@ -84,6 +86,7 @@ func (rf *RoomFeatures) Clone() *RoomFeatures { clone.DeleteMaxAge = ptr.Clone(clone.DeleteMaxAge) clone.DisappearingTimer = clone.DisappearingTimer.Clone() clone.AllowedReactions = slices.Clone(clone.AllowedReactions) + clone.MessageRequest = clone.MessageRequest.Clone() return &clone } @@ -165,6 +168,25 @@ func (dtc *DisappearingTimerCapability) Supports(content *BeeperDisappearingTime return slices.Contains(dtc.Types, content.Type) && (dtc.Timers == nil || slices.Contains(dtc.Timers, content.Timer)) } +type MessageRequestFeatures struct { + AcceptWithMessage CapabilitySupportLevel `json:"accept_with_message,omitempty"` + AcceptWithButton CapabilitySupportLevel `json:"accept_with_button,omitempty"` +} + +func (mrf *MessageRequestFeatures) Clone() *MessageRequestFeatures { + return ptr.Clone(mrf) +} + +func (mrf *MessageRequestFeatures) Hash() []byte { + if mrf == nil { + return nil + } + hasher := sha256.New() + hashValue(hasher, "accept_with_message", mrf.AcceptWithMessage) + hashValue(hasher, "accept_with_button", mrf.AcceptWithButton) + return hasher.Sum(nil) +} + type CapabilityMsgType = MessageType // Message types which are used for event capability signaling, but aren't real values for the msgtype field. @@ -347,6 +369,7 @@ func (rf *RoomFeatures) Hash() []byte { hashBool(hasher, "mark_as_unread", rf.MarkAsUnread) hashBool(hasher, "delete_chat", rf.DeleteChat) hashBool(hasher, "delete_chat_for_everyone", rf.DeleteChatForEveryone) + hashValue(hasher, "message_request", rf.MessageRequest) return hasher.Sum(nil) } diff --git a/vendor/maunium.net/go/mautrix/event/content.go b/vendor/maunium.net/go/mautrix/event/content.go index c0ff51a..4929c6a 100644 --- a/vendor/maunium.net/go/mautrix/event/content.go +++ b/vendor/maunium.net/go/mautrix/event/content.go @@ -61,9 +61,11 @@ var TypeMap = map[Type]reflect.Type{ EventUnstablePollStart: reflect.TypeOf(PollStartEventContent{}), EventUnstablePollResponse: reflect.TypeOf(PollResponseEventContent{}), - BeeperMessageStatus: reflect.TypeOf(BeeperMessageStatusEventContent{}), - BeeperTranscription: reflect.TypeOf(BeeperTranscriptionEventContent{}), - BeeperDeleteChat: reflect.TypeOf(BeeperChatDeleteEventContent{}), + BeeperMessageStatus: reflect.TypeOf(BeeperMessageStatusEventContent{}), + BeeperTranscription: reflect.TypeOf(BeeperTranscriptionEventContent{}), + BeeperDeleteChat: reflect.TypeOf(BeeperChatDeleteEventContent{}), + BeeperAcceptMessageRequest: reflect.TypeOf(BeeperAcceptMessageRequestEventContent{}), + BeeperSendState: reflect.TypeOf(BeeperSendStateEventContent{}), AccountDataRoomTags: reflect.TypeOf(TagEventContent{}), AccountDataDirectChats: reflect.TypeOf(DirectChatsEventContent{}), diff --git a/vendor/maunium.net/go/mautrix/event/encryption.go b/vendor/maunium.net/go/mautrix/event/encryption.go index cf9c281..e07944a 100644 --- a/vendor/maunium.net/go/mautrix/event/encryption.go +++ b/vendor/maunium.net/go/mautrix/event/encryption.go @@ -63,7 +63,7 @@ func (content *EncryptedEventContent) UnmarshalJSON(data []byte) error { return json.Unmarshal(content.Ciphertext, &content.OlmCiphertext) case id.AlgorithmMegolmV1: if len(content.Ciphertext) == 0 || content.Ciphertext[0] != '"' || content.Ciphertext[len(content.Ciphertext)-1] != '"' { - return id.InputNotJSONString + return id.ErrInputNotJSONString } content.MegolmCiphertext = content.Ciphertext[1 : len(content.Ciphertext)-1] } diff --git a/vendor/maunium.net/go/mautrix/event/state.go b/vendor/maunium.net/go/mautrix/event/state.go index 6df3b14..29e0e52 100644 --- a/vendor/maunium.net/go/mautrix/event/state.go +++ b/vendor/maunium.net/go/mautrix/event/state.go @@ -231,7 +231,8 @@ type BridgeInfoSection struct { AvatarURL id.ContentURIString `json:"avatar_url,omitempty"` ExternalURL string `json:"external_url,omitempty"` - Receiver string `json:"fi.mau.receiver,omitempty"` + Receiver string `json:"fi.mau.receiver,omitempty"` + MessageRequest bool `json:"com.beeper.message_request,omitempty"` } // BridgeEventContent represents the content of a m.bridge state event. diff --git a/vendor/maunium.net/go/mautrix/event/type.go b/vendor/maunium.net/go/mautrix/event/type.go index 56ea82f..f4d7592 100644 --- a/vendor/maunium.net/go/mautrix/event/type.go +++ b/vendor/maunium.net/go/mautrix/event/type.go @@ -128,7 +128,7 @@ func (et *Type) GuessClass() TypeClass { InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type, CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type, CallNegotiate.Type, CallHangup.Type, BeeperMessageStatus.Type, EventUnstablePollStart.Type, EventUnstablePollResponse.Type, - EventUnstablePollEnd.Type, BeeperTranscription.Type, BeeperDeleteChat.Type: + EventUnstablePollEnd.Type, BeeperTranscription.Type, BeeperDeleteChat.Type, BeeperAcceptMessageRequest.Type: return MessageEventType case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type, ToDeviceRoomKeyWithheld.Type, ToDeviceBeeperRoomKeyAck.Type: @@ -234,9 +234,11 @@ var ( CallNegotiate = Type{"m.call.negotiate", MessageEventType} CallHangup = Type{"m.call.hangup", MessageEventType} - BeeperMessageStatus = Type{"com.beeper.message_send_status", MessageEventType} - BeeperTranscription = Type{"com.beeper.transcription", MessageEventType} - BeeperDeleteChat = Type{"com.beeper.delete_chat", MessageEventType} + BeeperMessageStatus = Type{"com.beeper.message_send_status", MessageEventType} + BeeperTranscription = Type{"com.beeper.transcription", MessageEventType} + BeeperDeleteChat = Type{"com.beeper.delete_chat", MessageEventType} + BeeperAcceptMessageRequest = Type{"com.beeper.accept_message_request", MessageEventType} + BeeperSendState = Type{"com.beeper.send_state", MessageEventType} EventUnstablePollStart = Type{Type: "org.matrix.msc3381.poll.start", Class: MessageEventType} EventUnstablePollResponse = Type{Type: "org.matrix.msc3381.poll.response", Class: MessageEventType} diff --git a/vendor/maunium.net/go/mautrix/filter.go b/vendor/maunium.net/go/mautrix/filter.go index c6c8211..54973da 100644 --- a/vendor/maunium.net/go/mautrix/filter.go +++ b/vendor/maunium.net/go/mautrix/filter.go @@ -57,7 +57,7 @@ type FilterPart struct { // Validate checks if the filter contains valid property values func (filter *Filter) Validate() error { if filter.EventFormat != EventFormatClient && filter.EventFormat != EventFormatFederation { - return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]") + return errors.New("bad event_format value") } return nil } diff --git a/vendor/maunium.net/go/mautrix/id/contenturi.go b/vendor/maunium.net/go/mautrix/id/contenturi.go index e6a313f..be45eb2 100644 --- a/vendor/maunium.net/go/mautrix/id/contenturi.go +++ b/vendor/maunium.net/go/mautrix/id/contenturi.go @@ -17,8 +17,14 @@ import ( ) var ( - InvalidContentURI = errors.New("invalid Matrix content URI") - InputNotJSONString = errors.New("input doesn't look like a JSON string") + ErrInvalidContentURI = errors.New("invalid Matrix content URI") + ErrInputNotJSONString = errors.New("input doesn't look like a JSON string") +) + +// Deprecated: use variables prefixed with Err +var ( + InvalidContentURI = ErrInvalidContentURI + InputNotJSONString = ErrInputNotJSONString ) // ContentURIString is a string that's expected to be a Matrix content URI. @@ -55,9 +61,9 @@ func ParseContentURI(uri string) (parsed ContentURI, err error) { if len(uri) == 0 { return } else if !strings.HasPrefix(uri, "mxc://") { - err = InvalidContentURI + err = ErrInvalidContentURI } else if index := strings.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 { - err = InvalidContentURI + err = ErrInvalidContentURI } else { parsed.Homeserver = uri[6 : 6+index] parsed.FileID = uri[6+index+1:] @@ -71,9 +77,9 @@ func ParseContentURIBytes(uri []byte) (parsed ContentURI, err error) { if len(uri) == 0 { return } else if !bytes.HasPrefix(uri, mxcBytes) { - err = InvalidContentURI + err = ErrInvalidContentURI } else if index := bytes.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 { - err = InvalidContentURI + err = ErrInvalidContentURI } else { parsed.Homeserver = string(uri[6 : 6+index]) parsed.FileID = string(uri[6+index+1:]) @@ -86,7 +92,7 @@ func (uri *ContentURI) UnmarshalJSON(raw []byte) (err error) { *uri = ContentURI{} return nil } else if len(raw) < 2 || raw[0] != '"' || raw[len(raw)-1] != '"' { - return InputNotJSONString + return ErrInputNotJSONString } parsed, err := ParseContentURIBytes(raw[1 : len(raw)-1]) if err != nil { diff --git a/vendor/maunium.net/go/mautrix/id/matrixuri.go b/vendor/maunium.net/go/mautrix/id/matrixuri.go index 8f5ec84..d5c78bc 100644 --- a/vendor/maunium.net/go/mautrix/id/matrixuri.go +++ b/vendor/maunium.net/go/mautrix/id/matrixuri.go @@ -54,7 +54,7 @@ var SigilToPathSegment = map[rune]string{ func (uri *MatrixURI) getQuery() url.Values { q := make(url.Values) - if uri.Via != nil && len(uri.Via) > 0 { + if len(uri.Via) > 0 { q["via"] = uri.Via } if len(uri.Action) > 0 { diff --git a/vendor/maunium.net/go/mautrix/id/userid.go b/vendor/maunium.net/go/mautrix/id/userid.go index 859d235..726a0d5 100644 --- a/vendor/maunium.net/go/mautrix/id/userid.go +++ b/vendor/maunium.net/go/mautrix/id/userid.go @@ -219,15 +219,15 @@ func DecodeUserLocalpart(str string) (string, error) { for i := 0; i < len(strBytes); i++ { b := strBytes[i] if !isValidByte(b) { - return "", fmt.Errorf("Byte pos %d: Invalid byte", i) + return "", fmt.Errorf("invalid encoded byte at position %d: %c", i, b) } if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _ if i+1 >= len(strBytes) { - return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i) + return "", fmt.Errorf("unexpected end of string after underscore at %d", i) } if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping - return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i) + return "", fmt.Errorf("unexpected byte %c after underscore at %d", strBytes[i+1], i) } if strBytes[i+1] == '_' { outputBuffer.WriteByte('_') @@ -237,7 +237,7 @@ func DecodeUserLocalpart(str string) (string, error) { i++ // skip next byte since we just handled it } else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8 if i+2 >= len(strBytes) { - return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i) + return "", fmt.Errorf("unexpected end of string after equals sign at %d", i) } dst := make([]byte, 1) _, err := hex.Decode(dst, strBytes[i+1:i+3]) diff --git a/vendor/maunium.net/go/mautrix/pushrules/action.go b/vendor/maunium.net/go/mautrix/pushrules/action.go index 9838e88..b5a884b 100644 --- a/vendor/maunium.net/go/mautrix/pushrules/action.go +++ b/vendor/maunium.net/go/mautrix/pushrules/action.go @@ -105,7 +105,7 @@ func (action *PushAction) UnmarshalJSON(raw []byte) error { if ok { action.Action = ActionSetTweak action.Tweak = PushActionTweak(tweak) - action.Value, _ = val["value"] + action.Value = val["value"] } } return nil diff --git a/vendor/maunium.net/go/mautrix/responses.go b/vendor/maunium.net/go/mautrix/responses.go index e7b6b75..d822c84 100644 --- a/vendor/maunium.net/go/mautrix/responses.go +++ b/vendor/maunium.net/go/mautrix/responses.go @@ -263,10 +263,7 @@ type RespMutualRooms struct { type RespRoomSummary struct { PublicRoomInfo - Membership event.Membership `json:"membership,omitempty"` - RoomVersion id.RoomVersion `json:"room_version,omitempty"` - Encryption id.Algorithm `json:"encryption,omitempty"` - AllowedRoomIDs []id.RoomID `json:"allowed_room_ids,omitempty"` + Membership event.Membership `json:"membership,omitempty"` UnstableRoomVersion id.RoomVersion `json:"im.nheko.summary.room_version,omitempty"` UnstableRoomVersionOld id.RoomVersion `json:"im.nheko.summary.version,omitempty"` @@ -685,6 +682,10 @@ type PublicRoomInfo struct { RoomType event.RoomType `json:"room_type"` Topic string `json:"topic,omitempty"` WorldReadable bool `json:"world_readable"` + + RoomVersion id.RoomVersion `json:"room_version,omitempty"` + Encryption id.Algorithm `json:"encryption,omitempty"` + AllowedRoomIDs []id.RoomID `json:"allowed_room_ids,omitempty"` } // RespHierarchy is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy diff --git a/vendor/maunium.net/go/mautrix/room.go b/vendor/maunium.net/go/mautrix/room.go index c3ddb7e..4292bff 100644 --- a/vendor/maunium.net/go/mautrix/room.go +++ b/vendor/maunium.net/go/mautrix/room.go @@ -5,8 +5,6 @@ import ( "maunium.net/go/mautrix/id" ) -type RoomStateMap = map[event.Type]map[string]*event.Event - // Room represents a single Matrix room. type Room struct { ID id.RoomID @@ -25,8 +23,8 @@ func (room Room) UpdateState(evt *event.Event) { // GetStateEvent returns the state event for the given type/state_key combo, or nil. func (room Room) GetStateEvent(eventType event.Type, stateKey string) *event.Event { - stateEventMap, _ := room.State[eventType] - evt, _ := stateEventMap[stateKey] + stateEventMap := room.State[eventType] + evt := stateEventMap[stateKey] return evt } diff --git a/vendor/maunium.net/go/mautrix/statestore.go b/vendor/maunium.net/go/mautrix/statestore.go index c6267c5..2bd498d 100644 --- a/vendor/maunium.net/go/mautrix/statestore.go +++ b/vendor/maunium.net/go/mautrix/statestore.go @@ -129,6 +129,7 @@ func NewMemoryStateStore() StateStore { PowerLevels: make(map[id.RoomID]*event.PowerLevelsEventContent), Encryption: make(map[id.RoomID]*event.EncryptionEventContent), Create: make(map[id.RoomID]*event.Event), + JoinRules: make(map[id.RoomID]*event.JoinRulesEventContent), } } diff --git a/vendor/maunium.net/go/mautrix/url.go b/vendor/maunium.net/go/mautrix/url.go index d888956..91b3d49 100644 --- a/vendor/maunium.net/go/mautrix/url.go +++ b/vendor/maunium.net/go/mautrix/url.go @@ -98,10 +98,8 @@ func (saup SynapseAdminURLPath) FullPath() []any { // and appservice user ID set already. func (cli *Client) BuildURLWithQuery(urlPath PrefixableURLPath, urlQuery map[string]string) string { return cli.BuildURLWithFullQuery(urlPath, func(q url.Values) { - if urlQuery != nil { - for k, v := range urlQuery { - q.Set(k, v) - } + for k, v := range urlQuery { + q.Set(k, v) } }) } diff --git a/vendor/maunium.net/go/mautrix/version.go b/vendor/maunium.net/go/mautrix/version.go index f6d20c3..46d3342 100644 --- a/vendor/maunium.net/go/mautrix/version.go +++ b/vendor/maunium.net/go/mautrix/version.go @@ -8,7 +8,7 @@ import ( "strings" ) -const Version = "v0.26.0" +const Version = "v0.26.1" var GoModVersion = "" var Commit = "" diff --git a/vendor/modernc.org/sqlite/AUTHORS b/vendor/modernc.org/sqlite/AUTHORS index 7b23676..9a3bfef 100644 --- a/vendor/modernc.org/sqlite/AUTHORS +++ b/vendor/modernc.org/sqlite/AUTHORS @@ -29,4 +29,4 @@ Saed SayedAhmed Steffen Butzer Toni Spets W. Michael Petullo -SUSE LLC +SUSE LLC \ No newline at end of file diff --git a/vendor/modernc.org/sqlite/CONTRIBUTORS b/vendor/modernc.org/sqlite/CONTRIBUTORS index e853421..7fc3587 100644 --- a/vendor/modernc.org/sqlite/CONTRIBUTORS +++ b/vendor/modernc.org/sqlite/CONTRIBUTORS @@ -10,6 +10,7 @@ Alexander Menzhinsky Alexey Palazhchenko Angus Dippenaar Artyom Pervukhin +Adrian Witas Dan Kortschak Dan Peterson David Skinner diff --git a/vendor/modernc.org/sqlite/README.md b/vendor/modernc.org/sqlite/README.md index 1616f91..c3a920a 100644 --- a/vendor/modernc.org/sqlite/README.md +++ b/vendor/modernc.org/sqlite/README.md @@ -1,7 +1,18 @@ +# ⚠️ Important: Repository Mirroring + +**This project is primarily developed on GitLab.** The repository you are currently viewing might be a mirror. Please review the guidelines below based on where you are viewing this: + +| Platform | Role | Contributing Guidelines | +| :--- | :--- | :--- | +| **GitLab** | **Primary Source** | This is the canonical repository (`cznic/sqlite`). CI pipelines and main development happen here. | +| **GitHub** | **Mirror** | This is a mirror (`modernc-org/sqlite`). We **do accept** Issues and Pull Requests here for your convenience!
*Note: PRs submitted here will be manually merged into the GitLab source, so please allow extra time for processing.* | + +--- + + ![logo](logo.png) [![Go Reference](https://pkg.go.dev/badge/modernc.org/sqlite.svg)](https://pkg.go.dev/modernc.org/sqlite) - [![LiberaPay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/jnml/donate) [![receives](https://img.shields.io/liberapay/receives/jnml.svg?logo=liberapay)](https://liberapay.com/jnml/donate) [![patrons](https://img.shields.io/liberapay/patrons/jnml.svg?logo=liberapay)](https://liberapay.com/jnml/donate) @@ -9,3 +20,31 @@ [The SQLite Drivers Benchmarks Game] [The SQLite Drivers Benchmarks Game]: https://pkg.go.dev/modernc.org/sqlite-bench#readme-tl-dr-scorecard + +Virtual Tables (vtab) +--------------------- + +The driver exposes a Go API to implement SQLite virtual table modules in pure Go via the `modernc.org/sqlite/vtab` package. This lets you back SQL tables with arbitrary data sources (e.g., vector indexes, CSV files, remote APIs) and integrate with SQLite’s planner. + +- Register: `vtab.RegisterModule(db, name, module)`. Registration applies to new connections only. +- Schema declaration: Call `ctx.Declare("CREATE TABLE ()")` within `Create` or `Connect`. The driver does not auto-declare schemas, enabling dynamic schemas. +- Module arguments: `args []string` passed to `Create/Connect` are configuration parsed from `USING module(...)`. They are not treated as columns unless your module chooses to. +- Planning (BestIndex): + - Inspect `info.Constraints` (with `Column`, `Op`, `Usable`, 0-based `ArgIndex`, and `Omit`), `info.OrderBy`, and `info.ColUsed` (bitmask of referenced columns). + - Set `ArgIndex` (0-based) to populate `Filter`’s `vals` in the chosen order; set `Omit` to ask SQLite not to re-check a constraint you fully handle. +- Execution: `Cursor.Filter(idxNum, idxStr, vals)` receives arguments in the order implied by `ArgIndex`. +- Operators: Common SQLite operators map to `ConstraintOp` (EQ/NE/GT/GE/LT/LE/MATCH/IS/ISNOT/ISNULL/ISNOTNULL/LIKE/GLOB/REGEXP/FUNCTION/LIMIT/OFFSET). Unknown operators map to `OpUnknown`. +- Errors: Returning an error from vtab methods surfaces a descriptive message to SQLite (e.g., `zErrMsg` for xCreate/xConnect/xBestIndex/xFilter; `sqlite3_result_error` for xColumn). + +Examples +-------- + +- Vector search (sqlite-vec style): + - `CREATE VIRTUAL TABLE vec_docs USING vec(dim=128, metric="cosine")` + - Module reads args (e.g., `dim`, `metric`), calls `ctx.Declare("CREATE TABLE vec_docs(id, embedding, content HIDDEN)")`, and implements search via `BestIndex`/`Filter`. + +- CSV loader: + - `CREATE VIRTUAL TABLE csv_users USING csv(filename="/tmp/users.csv", delimiter=",", header=true)` + - Module reads the file header to compute columns, declares them via `ctx.Declare("CREATE TABLE csv_users(name, email, ...)")`, and streams rows via a cursor. + +See `vtab` package docs for full API details. diff --git a/vendor/modernc.org/sqlite/backup.go b/vendor/modernc.org/sqlite/backup.go new file mode 100644 index 0000000..24c1590 --- /dev/null +++ b/vendor/modernc.org/sqlite/backup.go @@ -0,0 +1,65 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "database/sql/driver" + + sqlite3 "modernc.org/sqlite/lib" +) + +// Backup object is used to manage progress and cleanup an online backup. It +// is returned by NewBackup or NewRestore. +type Backup struct { + srcConn *conn // source database connection + dstConn *conn // destination database connection + pBackup uintptr // sqlite3_backup object pointer +} + +// Step will copy up to n pages between the source and destination databases +// specified by the backup object. If n is negative, all remaining source +// pages are copied. +// If it successfully copies n pages and there are still more pages to be +// copied, then the function returns true with no error. If it successfully +// finishes copying all pages from source to destination, then it returns +// false with no error. If an error occurs while running, then an error is +// returned. +func (b *Backup) Step(n int32) (bool, error) { + rc := sqlite3.Xsqlite3_backup_step(b.srcConn.tls, b.pBackup, n) + if rc == sqlite3.SQLITE_OK { + return true, nil + } else if rc == sqlite3.SQLITE_DONE { + return false, nil + } else { + return false, b.srcConn.errstr(rc) + } +} + +// Finish releases all resources associated with the Backup object. The Backup +// object is invalid and may not be used following a call to Finish. +func (b *Backup) Finish() error { + rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup) + b.dstConn.Close() + if rc == sqlite3.SQLITE_OK { + return nil + } else { + return b.srcConn.errstr(rc) + } +} + +// Commit releases all resources associated with the Backup object but does not +// close the destination database connection. +// +// The destination database connection is returned to the caller or an error if raised. +// It is the responsibility of the caller to handle the connection closure. +func (b *Backup) Commit() (driver.Conn, error) { + rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup) + + if rc == sqlite3.SQLITE_OK { + return b.dstConn, nil + } else { + return nil, b.srcConn.errstr(rc) + } +} diff --git a/vendor/modernc.org/sqlite/conn.go b/vendor/modernc.org/sqlite/conn.go new file mode 100644 index 0000000..bd0e2a1 --- /dev/null +++ b/vendor/modernc.org/sqlite/conn.go @@ -0,0 +1,1070 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "context" + "database/sql/driver" + "fmt" + "strconv" + "strings" + "sync" + "time" + "unsafe" + + "modernc.org/libc" + "modernc.org/libc/sys/types" + sqlite3 "modernc.org/sqlite/lib" +) + +type conn struct { + db uintptr // *sqlite3.Xsqlite3 + tls *libc.TLS + + // Context handling can cause conn.Close and conn.interrupt to be invoked + // concurrently. + sync.Mutex + + writeTimeFormat string + beginMode string + intToTime bool + integerTimeFormat string +} + +func newConn(dsn string) (*conn, error) { + var query, vfsName string + + // Parse the query parameters from the dsn and them from the dsn if not prefixed by file: + // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1046 + // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1383 + pos := strings.IndexRune(dsn, '?') + if pos >= 1 { + query = dsn[pos+1:] + var err error + vfsName, err = getVFSName(query) + if err != nil { + return nil, err + } + + if !strings.HasPrefix(dsn, "file:") { + dsn = dsn[:pos] + } + } + + c := &conn{tls: libc.NewTLS()} + db, err := c.openV2( + dsn, + vfsName, + sqlite3.SQLITE_OPEN_READWRITE|sqlite3.SQLITE_OPEN_CREATE| + sqlite3.SQLITE_OPEN_FULLMUTEX| + sqlite3.SQLITE_OPEN_URI, + ) + if err != nil { + return nil, err + } + + c.db = db + if err = c.extendedResultCodes(true); err != nil { + c.Close() + return nil, err + } + + if err = applyQueryParams(c, query); err != nil { + c.Close() + return nil, err + } + + return c, nil +} + +// Attempt to parse s as a time. Return (s, false) if s is not +// recognized as a valid time encoding. +func (c *conn) parseTime(s string) (interface{}, bool) { + if v, ok := c.parseTimeString(s, strings.Index(s, "m=")); ok { + return v, true + } + + ts := strings.TrimSuffix(s, "Z") + + for _, f := range parseTimeFormats { + t, err := time.Parse(f, ts) + if err == nil { + return t, true + } + } + + return s, false +} + +// Attempt to parse s as a time string produced by t.String(). If x > 0 it's +// the index of substring "m=" within s. Return (s, false) if s is +// not recognized as a valid time encoding. +func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) { + s := s0 + if x > 0 { + s = s[:x] // "2006-01-02 15:04:05.999999999 -0700 MST m=+9999" -> "2006-01-02 15:04:05.999999999 -0700 MST " + } + s = strings.TrimSpace(s) + if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil { + return t, true + } + + return s0, false +} + +// writeTimeFormats are the names and formats supported +// by the `_time_format` DSN query param. +var writeTimeFormats = map[string]string{ + "sqlite": parseTimeFormats[0], +} + +func (c *conn) formatTime(t time.Time) string { + // Before configurable write time formats were supported, + // time.Time.String was used. Maintain that default to + // keep existing driver users formatting times the same. + if c.writeTimeFormat == "" { + return t.String() + } + return t.Format(c.writeTimeFormat) +} + +// C documentation +// +// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +func (c *conn) columnBlob(pstmt uintptr, iCol int) (v []byte, err error) { + p := sqlite3.Xsqlite3_column_blob(c.tls, pstmt, int32(iCol)) + len, err := c.columnBytes(pstmt, iCol) + if err != nil { + return nil, err + } + + if p == 0 || len == 0 { + return nil, nil + } + + v = make([]byte, len) + copy(v, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) + return v, nil +} + +// C documentation +// +// int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +func (c *conn) columnBytes(pstmt uintptr, iCol int) (_ int, err error) { + v := sqlite3.Xsqlite3_column_bytes(c.tls, pstmt, int32(iCol)) + return int(v), nil +} + +// C documentation +// +// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +func (c *conn) columnText(pstmt uintptr, iCol int) (v string, err error) { + p := sqlite3.Xsqlite3_column_text(c.tls, pstmt, int32(iCol)) + len, err := c.columnBytes(pstmt, iCol) + if err != nil { + return "", err + } + + if p == 0 || len == 0 { + return "", nil + } + + b := make([]byte, len) + copy(b, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) + return string(b), nil +} + +// C documentation +// +// double sqlite3_column_double(sqlite3_stmt*, int iCol); +func (c *conn) columnDouble(pstmt uintptr, iCol int) (v float64, err error) { + v = sqlite3.Xsqlite3_column_double(c.tls, pstmt, int32(iCol)) + return v, nil +} + +// C documentation +// +// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +func (c *conn) columnInt64(pstmt uintptr, iCol int) (v int64, err error) { + v = sqlite3.Xsqlite3_column_int64(c.tls, pstmt, int32(iCol)) + return v, nil +} + +// C documentation +// +// int sqlite3_column_type(sqlite3_stmt*, int iCol); +func (c *conn) columnType(pstmt uintptr, iCol int) (_ int, err error) { + v := sqlite3.Xsqlite3_column_type(c.tls, pstmt, int32(iCol)) + return int(v), nil +} + +// C documentation +// +// const char *sqlite3_column_decltype(sqlite3_stmt*,int); +func (c *conn) columnDeclType(pstmt uintptr, iCol int) string { + return libc.GoString(sqlite3.Xsqlite3_column_decltype(c.tls, pstmt, int32(iCol))) +} + +// C documentation +// +// const char *sqlite3_column_name(sqlite3_stmt*, int N); +func (c *conn) columnName(pstmt uintptr, n int) (string, error) { + p := sqlite3.Xsqlite3_column_name(c.tls, pstmt, int32(n)) + return libc.GoString(p), nil +} + +// C documentation +// +// int sqlite3_column_count(sqlite3_stmt *pStmt); +func (c *conn) columnCount(pstmt uintptr) (_ int, err error) { + v := sqlite3.Xsqlite3_column_count(c.tls, pstmt) + return int(v), nil +} + +// C documentation +// +// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +func (c *conn) lastInsertRowID() (v int64, _ error) { + return sqlite3.Xsqlite3_last_insert_rowid(c.tls, c.db), nil +} + +// C documentation +// +// int sqlite3_changes(sqlite3*); +func (c *conn) changes() (int, error) { + v := sqlite3.Xsqlite3_changes(c.tls, c.db) + return int(v), nil +} + +// C documentation +// +// int sqlite3_step(sqlite3_stmt*); +func (c *conn) step(pstmt uintptr) (int, error) { + for { + switch rc := sqlite3.Xsqlite3_step(c.tls, pstmt); rc { + case sqliteLockedSharedcache: + if err := c.retry(pstmt); err != nil { + return sqlite3.SQLITE_LOCKED, err + } + case + sqlite3.SQLITE_DONE, + sqlite3.SQLITE_ROW: + + return int(rc), nil + default: + return int(rc), c.errstr(rc) + } + } +} + +func (c *conn) retry(pstmt uintptr) error { + mu := mutexAlloc(c.tls) + (*mutex)(unsafe.Pointer(mu)).Lock() + rc := sqlite3.Xsqlite3_unlock_notify( + c.tls, + c.db, + *(*uintptr)(unsafe.Pointer(&struct { + f func(*libc.TLS, uintptr, int32) + }{unlockNotify})), + mu, + ) + if rc == sqlite3.SQLITE_LOCKED { // Deadlock, see https://www.sqlite.org/c3ref/unlock_notify.html + (*mutex)(unsafe.Pointer(mu)).Unlock() + mutexFree(c.tls, mu) + return c.errstr(rc) + } + + (*mutex)(unsafe.Pointer(mu)).Lock() + (*mutex)(unsafe.Pointer(mu)).Unlock() + mutexFree(c.tls, mu) + if pstmt != 0 { + sqlite3.Xsqlite3_reset(c.tls, pstmt) + } + return nil +} + +func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []uintptr, err error) { + defer func() { + if err == nil { + return + } + + for _, v := range allocs { + c.free(v) + } + allocs = nil + }() + + for i := 1; i <= n; i++ { + name, err := c.bindParameterName(pstmt, i) + if err != nil { + return allocs, err + } + + var found bool + var v driver.NamedValue + for _, v = range args { + if name != "" { + // For ?NNN and $NNN params, match if NNN == v.Ordinal. + // + // Supporting this for $NNN is a special case that makes eg + // `select $1, $2, $3 ...` work without needing to use + // sql.Named. + if (name[0] == '?' || name[0] == '$') && name[1:] == strconv.Itoa(v.Ordinal) { + found = true + break + } + + // sqlite supports '$', '@' and ':' prefixes for string + // identifiers and '?' for numeric, so we cannot + // combine different prefixes with the same name + // because `database/sql` requires variable names + // to start with a letter + if name[1:] == v.Name[:] { + found = true + break + } + } else { + if v.Ordinal == i { + found = true + break + } + } + } + + if !found { + if name != "" { + return allocs, fmt.Errorf("missing named argument %q", name[1:]) + } + + return allocs, fmt.Errorf("missing argument with index %d", i) + } + + var p uintptr + switch x := v.Value.(type) { + case int64: + if err := c.bindInt64(pstmt, i, x); err != nil { + return allocs, err + } + case float64: + if err := c.bindDouble(pstmt, i, x); err != nil { + return allocs, err + } + case bool: + v := 0 + if x { + v = 1 + } + if err := c.bindInt(pstmt, i, v); err != nil { + return allocs, err + } + case []byte: + if p, err = c.bindBlob(pstmt, i, x); err != nil { + return allocs, err + } + case string: + if p, err = c.bindText(pstmt, i, x); err != nil { + return allocs, err + } + case time.Time: + switch c.integerTimeFormat { + case "unix": + if err := c.bindInt64(pstmt, i, x.Unix()); err != nil { + return allocs, err + } + case "unix_milli": + if err := c.bindInt64(pstmt, i, x.UnixMilli()); err != nil { + return allocs, err + } + case "unix_micro": + if err := c.bindInt64(pstmt, i, x.UnixMicro()); err != nil { + return allocs, err + } + case "unix_nano": + if err := c.bindInt64(pstmt, i, x.UnixNano()); err != nil { + return allocs, err + } + default: + if p, err = c.bindText(pstmt, i, c.formatTime(x)); err != nil { + return allocs, err + } + } + + case nil: + if p, err = c.bindNull(pstmt, i); err != nil { + return allocs, err + } + default: + return allocs, fmt.Errorf("sqlite: invalid driver.Value type %T", x) + } + if p != 0 { + allocs = append(allocs, p) + } + } + return allocs, nil +} + +// C documentation +// +// int sqlite3_bind_null(sqlite3_stmt*, int); +func (c *conn) bindNull(pstmt uintptr, idx1 int) (uintptr, error) { + if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { + return 0, c.errstr(rc) + } + + return 0, nil +} + +// C documentation +// +// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +func (c *conn) bindText(pstmt uintptr, idx1 int, value string) (uintptr, error) { + p, err := libc.CString(value) + if err != nil { + return 0, err + } + + if rc := sqlite3.Xsqlite3_bind_text(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { + c.free(p) + return 0, c.errstr(rc) + } + + return p, nil +} + +// C documentation +// +// int sqlite3_bind_int(sqlite3_stmt*, int, int); +func (c *conn) bindInt(pstmt uintptr, idx1, value int) (err error) { + if rc := sqlite3.Xsqlite3_bind_int(c.tls, pstmt, int32(idx1), int32(value)); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + + return nil +} + +// C documentation +// +// int sqlite3_bind_double(sqlite3_stmt*, int, double); +func (c *conn) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) { + if rc := sqlite3.Xsqlite3_bind_double(c.tls, pstmt, int32(idx1), value); rc != 0 { + return c.errstr(rc) + } + + return nil +} + +// C documentation +// +// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +func (c *conn) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) { + if rc := sqlite3.Xsqlite3_bind_int64(c.tls, pstmt, int32(idx1), value); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + + return nil +} + +// C documentation +// +// const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +func (c *conn) bindParameterName(pstmt uintptr, i int) (string, error) { + p := sqlite3.Xsqlite3_bind_parameter_name(c.tls, pstmt, int32(i)) + return libc.GoString(p), nil +} + +// C documentation +// +// int sqlite3_bind_parameter_count(sqlite3_stmt*); +func (c *conn) bindParameterCount(pstmt uintptr) (_ int, err error) { + r := sqlite3.Xsqlite3_bind_parameter_count(c.tls, pstmt) + return int(r), nil +} + +// C documentation +// +// int sqlite3_finalize(sqlite3_stmt *pStmt); +func (c *conn) finalize(pstmt uintptr) error { + if rc := sqlite3.Xsqlite3_finalize(c.tls, pstmt); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + + return nil +} + +// C documentation +// +// int sqlite3_prepare_v2( +// sqlite3 *db, /* Database handle */ +// const char *zSql, /* SQL statement, UTF-8 encoded */ +// int nByte, /* Maximum length of zSql in bytes. */ +// sqlite3_stmt **ppStmt, /* OUT: Statement handle */ +// const char **pzTail /* OUT: Pointer to unused portion of zSql */ +// ); +func (c *conn) prepareV2(zSQL *uintptr) (pstmt uintptr, err error) { + var ppstmt, pptail uintptr + + defer func() { + c.free(ppstmt) + c.free(pptail) + }() + + if ppstmt, err = c.malloc(int(ptrSize)); err != nil { + return 0, err + } + + if pptail, err = c.malloc(int(ptrSize)); err != nil { + return 0, err + } + + for { + // https://gitlab.com/cznic/sqlite/-/issues/236 + // trc("Xsqlite3_prepare_v2(`%s`)", libc.GoString(*zSQL)) + switch rc := sqlite3.Xsqlite3_prepare_v2(c.tls, c.db, *zSQL, -1, ppstmt, pptail); rc { + case sqlite3.SQLITE_OK: + *zSQL = *(*uintptr)(unsafe.Pointer(pptail)) + return *(*uintptr)(unsafe.Pointer(ppstmt)), nil + case sqliteLockedSharedcache: + if err := c.retry(0); err != nil { + return 0, err + } + default: + return 0, c.errstr(rc) + } + } +} + +// C documentation +// +// void sqlite3_interrupt(sqlite3*); +func (c *conn) interrupt(pdb uintptr) (err error) { + c.Lock() // Defend against race with .Close invoked by context handling. + + defer c.Unlock() + + if c.tls != nil { + sqlite3.Xsqlite3_interrupt(c.tls, pdb) + } + return nil +} + +// C documentation +// +// int sqlite3_extended_result_codes(sqlite3*, int onoff); +func (c *conn) extendedResultCodes(on bool) error { + if rc := sqlite3.Xsqlite3_extended_result_codes(c.tls, c.db, libc.Bool32(on)); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + + return nil +} + +// C documentation +// +// int sqlite3_open_v2( +// const char *filename, /* Database filename (UTF-8) */ +// sqlite3 **ppDb, /* OUT: SQLite db handle */ +// int flags, /* Flags */ +// const char *zVfs /* Name of VFS module to use */ +// ); +func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) { + var p, s, vfs uintptr + + defer func() { + if p != 0 { + c.free(p) + } + if s != 0 { + c.free(s) + } + if vfs != 0 { + c.free(vfs) + } + }() + + p, err := c.malloc(int(ptrSize)) + if err != nil { + return 0, err + } + + if s, err = libc.CString(name); err != nil { + return 0, err + } + + if vfsName != "" { + if vfs, err = libc.CString(vfsName); err != nil { + return 0, err + } + } + + if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK { + return 0, c.errstr(rc) + } + + return *(*uintptr)(unsafe.Pointer(p)), nil +} + +func (c *conn) malloc(n int) (uintptr, error) { + if p := libc.Xmalloc(c.tls, types.Size_t(n)); p != 0 || n == 0 { + return p, nil + } + + return 0, fmt.Errorf("sqlite: cannot allocate %d bytes of memory", n) +} + +func (c *conn) free(p uintptr) { + if p != 0 { + libc.Xfree(c.tls, p) + } +} + +// C documentation +// +// const char *sqlite3_errstr(int); +func (c *conn) errstr(rc int32) error { + p := sqlite3.Xsqlite3_errstr(c.tls, rc) + str := libc.GoString(p) + p = sqlite3.Xsqlite3_errmsg(c.tls, c.db) + var s string + if rc == sqlite3.SQLITE_BUSY { + s = " (SQLITE_BUSY)" + } + switch msg := libc.GoString(p); { + case msg == str: + return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)} + default: + return &Error{msg: fmt.Sprintf("%s: %s (%v)%s", str, msg, rc, s), code: int(rc)} + } +} + +// Begin starts a transaction. +// +// Deprecated: Drivers should implement ConnBeginTx instead (or additionally). +func (c *conn) Begin() (dt driver.Tx, err error) { + if dmesgs { + defer func() { + dmesg("conn %p: (driver.Tx %p, err %v)", c, dt, err) + }() + } + return c.begin(context.Background(), driver.TxOptions{}) +} + +func (c *conn) begin(ctx context.Context, opts driver.TxOptions) (t driver.Tx, err error) { + return newTx(ctx, c, opts) +} + +// Close invalidates and potentially stops any current prepared statements and +// transactions, marking this connection as no longer in use. +// +// Because the sql package maintains a free pool of connections and only calls +// Close when there's a surplus of idle connections, it shouldn't be necessary +// for drivers to do their own connection caching. +func (c *conn) Close() (err error) { + if dmesgs { + defer func() { + dmesg("conn %p: err %v", c, err) + }() + } + c.Lock() // Defend against race with .interrupt invoked by context handling. + + defer c.Unlock() + + if c.db != 0 { + if err := c.closeV2(c.db); err != nil { + return err + } + + c.db = 0 + } + + if c.tls != nil { + c.tls.Close() + c.tls = nil + } + return nil +} + +// C documentation +// +// int sqlite3_close_v2(sqlite3*); +func (c *conn) closeV2(db uintptr) error { + if rc := sqlite3.Xsqlite3_close_v2(c.tls, db); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + + return nil +} + +// ResetSession is called prior to executing a query on the connection if the +// connection has been used before. If the driver returns ErrBadConn the +// connection is discarded. +func (c *conn) ResetSession(ctx context.Context) error { + if !c.usable() { + return driver.ErrBadConn + } + + return nil +} + +// IsValid is called prior to placing the connection into the connection pool. +// The connection will be discarded if false is returned. +func (c *conn) IsValid() bool { + return c.usable() +} + +func (c *conn) usable() bool { + return c.db != 0 && sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0 +} + +type userDefinedFunction struct { + zFuncName uintptr + nArg int32 + eTextRep int32 + pApp uintptr + + scalar bool + freeOnce sync.Once +} + +func (c *conn) createFunctionInternal(fun *userDefinedFunction) error { + var rc int32 + + if fun.scalar { + rc = sqlite3.Xsqlite3_create_function( + c.tls, + c.db, + fun.zFuncName, + fun.nArg, + fun.eTextRep, + fun.pApp, + cFuncPointer(funcTrampoline), + 0, + 0, + ) + } else { + rc = sqlite3.Xsqlite3_create_window_function( + c.tls, + c.db, + fun.zFuncName, + fun.nArg, + fun.eTextRep, + fun.pApp, + cFuncPointer(stepTrampoline), + cFuncPointer(finalTrampoline), + cFuncPointer(valueTrampoline), + cFuncPointer(inverseTrampoline), + 0, + ) + } + + if rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + return nil +} + +func (c *conn) createCollationInternal(coll *collation) error { + rc := sqlite3.Xsqlite3_create_collation_v2( + c.tls, + c.db, + coll.zName, + coll.enc, + coll.pApp, + cFuncPointer(collationTrampoline), + 0, + ) + if rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + return nil +} + +// Execer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Execer, the sql package's DB.Exec will first +// prepare a query, execute the statement, and then close the statement. +// +// Exec may return ErrSkip. +// +// Deprecated: Drivers should implement ExecerContext instead. +func (c *conn) Exec(query string, args []driver.Value) (dr driver.Result, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, query %q, args %v: (driver.Result %p, err %v)", c, query, args, dr, err) + }() + } + return c.exec(context.Background(), query, toNamedValues(args)) +} + +func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { + s, err := c.prepare(ctx, query) + if err != nil { + return nil, err + } + + defer func() { + if err2 := s.Close(); err2 != nil && err == nil { + err = err2 + } + }() + + return s.(*stmt).exec(ctx, args) +} + +// Prepare returns a prepared statement, bound to this connection. +func (c *conn) Prepare(query string) (ds driver.Stmt, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, query %q: (driver.Stmt %p, err %v)", c, query, ds, err) + }() + } + return c.prepare(context.Background(), query) +} + +func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { + //TODO use ctx + return newStmt(c, query) +} + +// Queryer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Queryer, the sql package's DB.Query will first +// prepare a query, execute the statement, and then close the statement. +// +// Query may return ErrSkip. +// +// Deprecated: Drivers should implement QueryerContext instead. +func (c *conn) Query(query string, args []driver.Value) (dr driver.Rows, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, query %q, args %v: (driver.Rows %p, err %v)", c, query, args, dr, err) + }() + } + return c.query(context.Background(), query, toNamedValues(args)) +} + +func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { + // Use newStmt directly. c.prepare wraps this, but we need the concrete *stmt type + // to manipulate the handle ownership below. + s, err := newStmt(c, query) + if err != nil { + return nil, err + } + + r, err = s.query(ctx, args) + if err != nil { + s.Close() + return nil, err + } + + // Fix for TestIssue118 / One-Shot Query Crash: + // If the statement was cached (optimized path in newStmt), s.pstmt is valid. + // s.query() sets r.reuseStmt = true by default for cached statements. + // + // However, since this is a transient statement created just for this query (db.Query), + // s.Close() will be called immediately below. We must transfer ownership of the + // sqlite statement handle to the rows object so it isn't finalized yet. + if s.pstmt != 0 { + // Steal the handle from the statement so s.Close() doesn't finalize it. + s.pstmt = 0 + + // Instruct rows to finalize the statement when done, rather than resetting it. + r.(*rows).reuseStmt = false + } + + // s.Close() now only frees the C-string allocation (psql), skipping the pstmt finalize + // because we set s.pstmt to 0. + if err := s.Close(); err != nil { + r.Close() + return nil, err + } + + return r, nil +} + +// Serialize returns a serialization of the main database. For an ordinary on-disk +// database file, the serialization is just a copy of the disk file. For an in-memory +// database or a "TEMP" database, the serialization is the same sequence of bytes +// which would be written to disk if that database where backed up to disk. +func (c *conn) Serialize() (v []byte, err error) { + pLen := c.tls.Alloc(8) + defer c.tls.Free(8) + + zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) + if zSchema == 0 { + return nil, fmt.Errorf("failed to get main db name") + } + + pBuf := sqlite3.Xsqlite3_serialize(c.tls, c.db, zSchema, pLen, 0) + bufLen := *(*sqlite3.Sqlite3_int64)(unsafe.Pointer(pLen)) + if pBuf != 0 { + defer sqlite3.Xsqlite3_free(c.tls, pBuf) + } + if bufLen <= 0 { + return nil, fmt.Errorf("invalid length returned: %d", bufLen) + } else if pBuf == 0 || bufLen == 0 { + return nil, nil + } + + v = make([]byte, bufLen) + copy(v, (*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen]) + return v, nil +} + +// Deserialize restore a database from the content returned by Serialize. +func (c *conn) Deserialize(buf []byte) (err error) { + bufLen := len(buf) + pBuf := c.tls.Alloc(bufLen) // free will be done if it fails or on close, must not be freed here + + copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf) + + zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) + if zSchema == 0 { + return fmt.Errorf("failed to get main db name") + } + + rc := sqlite3.Xsqlite3_deserialize(c.tls, c.db, zSchema, pBuf, int64(bufLen), int64(bufLen), sqlite3.SQLITE_DESERIALIZE_RESIZEABLE|sqlite3.SQLITE_DESERIALIZE_FREEONCLOSE) + if rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + return nil +} + +// NewBackup returns a Backup object that will create an online backup of +// current database to the databased pointed by the passed URI. +func (c *conn) NewBackup(dstUri string) (*Backup, error) { + dstConn, err := newConn(dstUri) + if err != nil { + return nil, err + } + backup, err := c.backup(dstConn, false) + if err != nil { + dstConn.Close() + } + return backup, err +} + +// NewRestore returns a Backup object that will restore a backup to current +// database from the databased pointed by the passed URI. +func (c *conn) NewRestore(srcUri string) (*Backup, error) { + srcConn, err := newConn(srcUri) + if err != nil { + return nil, err + } + backup, err := c.backup(srcConn, true) + if err != nil { + srcConn.Close() + } + return backup, err +} + +func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error) { + srcSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) + if srcSchema == 0 { + return nil, fmt.Errorf("failed to get main source db name") + } + + dstSchema := sqlite3.Xsqlite3_db_name(remoteConn.tls, remoteConn.db, 0) + if dstSchema == 0 { + return nil, fmt.Errorf("failed to get main destination db name") + } + + var pBackup uintptr + if restore { + pBackup = sqlite3.Xsqlite3_backup_init(c.tls, c.db, srcSchema, remoteConn.db, dstSchema) + } else { + pBackup = sqlite3.Xsqlite3_backup_init(c.tls, remoteConn.db, dstSchema, c.db, srcSchema) + } + if pBackup <= 0 { + rc := sqlite3.Xsqlite3_errcode(c.tls, remoteConn.db) + return nil, c.errstr(rc) + } + + return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil +} + +// C documentation +// +// int sqlite3_limit(sqlite3*, int id, int newVal); +func (c *conn) limit(id int, newVal int) int { + return int(sqlite3.Xsqlite3_limit(c.tls, c.db, int32(id), int32(newVal))) +} + +// C documentation +// +// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) { + if value == nil { + if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { + return 0, c.errstr(rc) + } + return 0, nil + } + + p, err := c.malloc(len(value)) + if err != nil { + return 0, err + } + if len(value) != 0 { + copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value) + } + if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { + c.free(p) + return 0, c.errstr(rc) + } + + return p, nil +} + +// Ping implements driver.Pinger +func (c *conn) Ping(ctx context.Context) (err error) { + if dmesgs { + defer func() { + dmesg("conn %p, ctx %p: err %v", c, ctx, err) + }() + } + _, err = c.ExecContext(ctx, "select 1", nil) + return err +} + +// BeginTx implements driver.ConnBeginTx +func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (dt driver.Tx, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, ctx %p, opts %+v: (driver.Tx %v, err %v)", c, ctx, opts, dt, err) + }() + } + return c.begin(ctx, opts) +} + +// PrepareContext implements driver.ConnPrepareContext +func (c *conn) PrepareContext(ctx context.Context, query string) (ds driver.Stmt, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, ctx %p, query %q: (driver.Stmt %v, err %v)", c, ctx, query, ds, err) + }() + } + return c.prepare(ctx, query) +} + +// ExecContext implements driver.ExecerContext +func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Result, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, ctx %p, query %q, args %v: (driver.Result %p, err %v)", c, ctx, query, args, dr, err) + }() + } + return c.exec(ctx, query, args) +} + +// QueryContext implements driver.QueryerContext +func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Rows, err error) { + if dmesgs { + defer func() { + dmesg("conn %p, ctx %p, query %q, args %v: (driver.Rows %p, err %v)", c, ctx, query, args, dr, err) + }() + } + return c.query(ctx, query, args) +} diff --git a/vendor/modernc.org/sqlite/doc.go b/vendor/modernc.org/sqlite/doc.go index 34c1037..4d58eb2 100644 --- a/vendor/modernc.org/sqlite/doc.go +++ b/vendor/modernc.org/sqlite/doc.go @@ -58,7 +58,7 @@ // - 2025-10-10 v1.39.1: Upgrade to SQLite 3.50.4. // // - 2025-06-09 v1.38.0: Upgrade to SQLite 3.50.1. -// +// // - 2025-02-26 v1.36.0: Upgrade to SQLite 3.49.0. // // - 2024-11-16 v1.34.0: Implement ResetSession and IsValid methods in connection diff --git a/vendor/modernc.org/sqlite/driver.go b/vendor/modernc.org/sqlite/driver.go new file mode 100644 index 0000000..0421200 --- /dev/null +++ b/vendor/modernc.org/sqlite/driver.go @@ -0,0 +1,120 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "database/sql/driver" + "fmt" + + "modernc.org/sqlite/vtab" +) + +// Driver implements database/sql/driver.Driver. +type Driver struct { + // user defined functions that are added to every new connection on Open + udfs map[string]*userDefinedFunction + // collations that are added to every new connection on Open + collations map[string]*collation + // connection hooks are called after a connection is opened + connectionHooks []ConnectionHookFn + // modules holds registered virtual table modules that should be added to + // every new connection on Open. + modules map[string]vtab.Module +} + +var d = &Driver{ + udfs: make(map[string]*userDefinedFunction, 0), + collations: make(map[string]*collation, 0), + connectionHooks: make([]ConnectionHookFn, 0), + modules: make(map[string]vtab.Module, 0), +} + +func newDriver() *Driver { return d } + +// Open returns a new connection to the database. The name is a string in a +// driver-specific format. +// +// Open may return a cached connection (one previously closed), but doing so is +// unnecessary; the sql package maintains a pool of idle connections for +// efficient re-use. +// +// The returned connection is only used by one goroutine at a time. +// +// The name may be a filename, e.g., "/tmp/mydata.sqlite", or a URI, in which +// case it may include a '?' followed by one or more query parameters. +// For example, "file:///tmp/mydata.sqlite?_pragma=foreign_keys(1)&_time_format=sqlite". +// The supported query parameters are: +// +// _pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA +// keyword added for you). May be specified more than once, '&'-separated. For more +// information on supported PRAGMAs see: https://www.sqlite.org/pragma.html +// +// _time_format: The name of a format to use when writing time values to the +// database. Currently the only supported value is "sqlite", which corresponds +// to format 7 from https://www.sqlite.org/lang_datefunc.html#time_values, +// including the timezone specifier. If this parameter is not specified, then +// the default String() format will be used. +// +// _time_integer_format: The name of a integer format to use when writing time values. +// By default, the time is stored as string and the format can be set with _time_format +// parameter. If _time_integer_format is set, the time will be stored as an integer and +// the integer value will depend on the integer format. +// If you decide to set both _time_format and _time_integer_format, the time will be +// converted as integer and the _time_format value will be ignored. +// Currently the supported value are "unix","unix_milli", "unix_micro" and "unix_nano", +// which corresponds to seconds, milliseconds, microseconds or nanoseconds +// since unixepoch (1 January 1970 00:00:00 UTC). +// +// _inttotime: Enable conversion of time column (DATE, DATETIME,TIMESTAMP) from integer +// to time if the field contain integer (int64). +// +// _txlock: The locking behavior to use when beginning a transaction. May be +// "deferred" (the default), "immediate", or "exclusive" (case insensitive). See: +// https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions +func (d *Driver) Open(name string) (conn driver.Conn, err error) { + if dmesgs { + defer func() { + dmesg("name %q: (driver.Conn %p, err %v)", name, conn, err) + }() + } + c, err := newConn(name) + if err != nil { + return nil, err + } + + for _, udf := range d.udfs { + if err = c.createFunctionInternal(udf); err != nil { + c.Close() + return nil, err + } + } + for _, coll := range d.collations { + if err = c.createCollationInternal(coll); err != nil { + c.Close() + return nil, err + } + } + for _, connHookFn := range d.connectionHooks { + if err = connHookFn(c, name); err != nil { + c.Close() + return nil, fmt.Errorf("connection hook: %w", err) + } + } + // Register any vtab modules with this connection. + // Note: vtab module registration applies to new connections only. If a + // module is registered after a connection has been opened, that existing + // connection will not see the module; open a new connection to use it. + if err := c.registerModules(); err != nil { + c.Close() + return nil, err + } + return c, nil +} + +// RegisterConnectionHook registers a function to be called after each connection +// is opened. This is called after all the connection has been set up. +func (d *Driver) RegisterConnectionHook(fn ConnectionHookFn) { + d.connectionHooks = append(d.connectionHooks, fn) +} diff --git a/vendor/modernc.org/sqlite/error.go b/vendor/modernc.org/sqlite/error.go new file mode 100644 index 0000000..a4f42f6 --- /dev/null +++ b/vendor/modernc.org/sqlite/error.go @@ -0,0 +1,75 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + sqlite3 "modernc.org/sqlite/lib" +) + +// Error represents sqlite library error code. +type Error struct { + msg string + code int +} + +// Error implements error. +func (e *Error) Error() string { return e.msg } + +// Code returns the sqlite result code for this error. +func (e *Error) Code() int { return e.code } + +var ( + // ErrorCodeString maps Error.Code() to its string representation. + ErrorCodeString = map[int]string{ + sqlite3.SQLITE_ABORT: "Callback routine requested an abort (SQLITE_ABORT)", + sqlite3.SQLITE_AUTH: "Authorization denied (SQLITE_AUTH)", + sqlite3.SQLITE_BUSY: "The database file is locked (SQLITE_BUSY)", + sqlite3.SQLITE_CANTOPEN: "Unable to open the database file (SQLITE_CANTOPEN)", + sqlite3.SQLITE_CONSTRAINT: "Abort due to constraint violation (SQLITE_CONSTRAINT)", + sqlite3.SQLITE_CORRUPT: "The database disk image is malformed (SQLITE_CORRUPT)", + sqlite3.SQLITE_DONE: "sqlite3_step() has finished executing (SQLITE_DONE)", + sqlite3.SQLITE_EMPTY: "Internal use only (SQLITE_EMPTY)", + sqlite3.SQLITE_ERROR: "Generic error (SQLITE_ERROR)", + sqlite3.SQLITE_FORMAT: "Not used (SQLITE_FORMAT)", + sqlite3.SQLITE_FULL: "Insertion failed because database is full (SQLITE_FULL)", + sqlite3.SQLITE_INTERNAL: "Internal logic error in SQLite (SQLITE_INTERNAL)", + sqlite3.SQLITE_INTERRUPT: "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)", + sqlite3.SQLITE_IOERR | (1 << 8): "(SQLITE_IOERR_READ)", + sqlite3.SQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)", + sqlite3.SQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)", + sqlite3.SQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)", + sqlite3.SQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)", + sqlite3.SQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)", + sqlite3.SQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)", + sqlite3.SQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)", + sqlite3.SQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)", + sqlite3.SQLITE_IOERR | (2 << 8): "(SQLITE_IOERR_SHORT_READ)", + sqlite3.SQLITE_IOERR | (3 << 8): "(SQLITE_IOERR_WRITE)", + sqlite3.SQLITE_IOERR | (4 << 8): "(SQLITE_IOERR_FSYNC)", + sqlite3.SQLITE_IOERR | (5 << 8): "(SQLITE_IOERR_DIR_FSYNC)", + sqlite3.SQLITE_IOERR | (6 << 8): "(SQLITE_IOERR_TRUNCATE)", + sqlite3.SQLITE_IOERR | (7 << 8): "(SQLITE_IOERR_FSTAT)", + sqlite3.SQLITE_IOERR | (8 << 8): "(SQLITE_IOERR_UNLOCK)", + sqlite3.SQLITE_IOERR | (9 << 8): "(SQLITE_IOERR_RDLOCK)", + sqlite3.SQLITE_IOERR: "Some kind of disk I/O error occurred (SQLITE_IOERR)", + sqlite3.SQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)", + sqlite3.SQLITE_LOCKED: "A table in the database is locked (SQLITE_LOCKED)", + sqlite3.SQLITE_MISMATCH: "Data type mismatch (SQLITE_MISMATCH)", + sqlite3.SQLITE_MISUSE: "Library used incorrectly (SQLITE_MISUSE)", + sqlite3.SQLITE_NOLFS: "Uses OS features not supported on host (SQLITE_NOLFS)", + sqlite3.SQLITE_NOMEM: "A malloc() failed (SQLITE_NOMEM)", + sqlite3.SQLITE_NOTADB: "File opened that is not a database file (SQLITE_NOTADB)", + sqlite3.SQLITE_NOTFOUND: "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)", + sqlite3.SQLITE_NOTICE: "Notifications from sqlite3_log() (SQLITE_NOTICE)", + sqlite3.SQLITE_PERM: "Access permission denied (SQLITE_PERM)", + sqlite3.SQLITE_PROTOCOL: "Database lock protocol error (SQLITE_PROTOCOL)", + sqlite3.SQLITE_RANGE: "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)", + sqlite3.SQLITE_READONLY: "Attempt to write a readonly database (SQLITE_READONLY)", + sqlite3.SQLITE_ROW: "sqlite3_step() has another row ready (SQLITE_ROW)", + sqlite3.SQLITE_SCHEMA: "The database schema changed (SQLITE_SCHEMA)", + sqlite3.SQLITE_TOOBIG: "String or BLOB exceeds size limit (SQLITE_TOOBIG)", + sqlite3.SQLITE_WARNING: "Warnings from sqlite3_log() (SQLITE_WARNING)", + } +) diff --git a/vendor/modernc.org/sqlite/lib/sqlite_darwin_amd64.go b/vendor/modernc.org/sqlite/lib/sqlite_darwin_amd64.go index 1079f5f..847d6a7 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_darwin_amd64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_darwin_amd64.go @@ -227735,4 +227735,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_darwin_arm64.go b/vendor/modernc.org/sqlite/lib/sqlite_darwin_arm64.go index 6a63273..df82bb9 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_darwin_arm64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_darwin_arm64.go @@ -227308,4 +227308,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_freebsd_amd64.go b/vendor/modernc.org/sqlite/lib/sqlite_freebsd_amd64.go index ffb049e..d553a52 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_freebsd_amd64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_freebsd_amd64.go @@ -221564,4 +221564,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_freebsd_arm64.go b/vendor/modernc.org/sqlite/lib/sqlite_freebsd_arm64.go index e32db26..238f333 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_freebsd_arm64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_freebsd_arm64.go @@ -221577,4 +221577,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_386.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_386.go index 8b34111..ce1dd56 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_386.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_386.go @@ -221713,4 +221713,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_amd64.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_amd64.go index 0f3932a..b67cafd 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_amd64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_amd64.go @@ -221726,4 +221726,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_arm.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_arm.go index 17ccb37..877ac9b 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_arm.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_arm.go @@ -222010,4 +222010,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_arm64.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_arm64.go index 8d7fd60..b1c0f9c 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_arm64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_arm64.go @@ -221716,4 +221716,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_loong64.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_loong64.go index 049a348..376d8b6 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_loong64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_loong64.go @@ -221815,4 +221815,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_ppc64le.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_ppc64le.go index 8117bdf..d2012c7 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_ppc64le.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_ppc64le.go @@ -221763,4 +221763,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_riscv64.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_riscv64.go index f3a6bca..5eeb2fb 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_riscv64.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_riscv64.go @@ -221709,4 +221709,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_linux_s390x.go b/vendor/modernc.org/sqlite/lib/sqlite_linux_s390x.go index 9b766c8..4821927 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_linux_s390x.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_linux_s390x.go @@ -221663,4 +221663,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_windows.go b/vendor/modernc.org/sqlite/lib/sqlite_windows.go index 5b3a68f..2dd9f66 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_windows.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_windows.go @@ -295017,4 +295017,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/lib/sqlite_windows_386.go b/vendor/modernc.org/sqlite/lib/sqlite_windows_386.go index 4cdc532..0a65da1 100644 --- a/vendor/modernc.org/sqlite/lib/sqlite_windows_386.go +++ b/vendor/modernc.org/sqlite/lib/sqlite_windows_386.go @@ -294909,4 +294909,3 @@ type Sqlite3_index_info = sqlite3_index_info type Sqlite3_module = sqlite3_module type Sqlite3_vtab = sqlite3_vtab type Sqlite3_vtab_cursor = sqlite3_vtab_cursor - diff --git a/vendor/modernc.org/sqlite/result.go b/vendor/modernc.org/sqlite/result.go new file mode 100644 index 0000000..84ca991 --- /dev/null +++ b/vendor/modernc.org/sqlite/result.go @@ -0,0 +1,42 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +type result struct { + lastInsertID int64 + rowsAffected int +} + +func newResult(c *conn) (_ *result, err error) { + r := &result{} + if r.rowsAffected, err = c.changes(); err != nil { + return nil, err + } + + if r.lastInsertID, err = c.lastInsertRowID(); err != nil { + return nil, err + } + + return r, nil +} + +// LastInsertId returns the database's auto-generated ID after, for example, an +// INSERT into a table with primary key. +func (r *result) LastInsertId() (int64, error) { + if r == nil { + return 0, nil + } + + return r.lastInsertID, nil +} + +// RowsAffected returns the number of rows affected by the query. +func (r *result) RowsAffected() (int64, error) { + if r == nil { + return 0, nil + } + + return int64(r.rowsAffected), nil +} diff --git a/vendor/modernc.org/sqlite/rows.go b/vendor/modernc.org/sqlite/rows.go new file mode 100644 index 0000000..3da7031 --- /dev/null +++ b/vendor/modernc.org/sqlite/rows.go @@ -0,0 +1,282 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "database/sql/driver" + "fmt" + "io" + "math" + "reflect" + "strings" + "time" + + sqlite3 "modernc.org/sqlite/lib" +) + +type rows struct { + allocs []uintptr + c *conn + columns []string + pstmt uintptr + + doStep bool + empty bool + reuseStmt bool // If true, Close() resets instead of finalizing +} + +func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err error) { + r = &rows{c: c, pstmt: pstmt, allocs: allocs, empty: empty} + + defer func() { + if err != nil { + r.Close() + r = nil + } + }() + + n, err := c.columnCount(pstmt) + if err != nil { + return nil, err + } + + r.columns = make([]string, n) + for i := range r.columns { + if r.columns[i], err = r.c.columnName(pstmt, i); err != nil { + return nil, err + } + } + + return r, nil +} + +// Close closes the rows iterator. +func (r *rows) Close() (err error) { + for _, v := range r.allocs { + r.c.free(v) + } + r.allocs = nil + + if r.reuseStmt { + // Reset the statement for reuse instead of finalizing it + if e := r.c.reset(r.pstmt); e != nil { + return e + } + return r.c.clearBindings(r.pstmt) + } + + return r.c.finalize(r.pstmt) +} + +// Columns returns the names of the columns. The number of columns of the +// result is inferred from the length of the slice. If a particular column name +// isn't known, an empty string should be returned for that entry. +func (r *rows) Columns() (c []string) { + return r.columns +} + +// Next is called to populate the next row of data into the provided slice. The +// provided slice will be the same size as the Columns() are wide. +// +// Next should return io.EOF when there are no more rows. +func (r *rows) Next(dest []driver.Value) (err error) { + if r.empty { + return io.EOF + } + + rc := sqlite3.SQLITE_ROW + if r.doStep { + if rc, err = r.c.step(r.pstmt); err != nil { + return err + } + } + + r.doStep = true + switch rc { + case sqlite3.SQLITE_ROW: + if g, e := len(dest), len(r.columns); g != e { + return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e) + } + + for i := range dest { + ct, err := r.c.columnType(r.pstmt, i) + if err != nil { + return err + } + + switch ct { + case sqlite3.SQLITE_INTEGER: + v, err := r.c.columnInt64(r.pstmt, i) + if err != nil { + return err + } + + if !r.c.intToTime { + dest[i] = v + } else { + // Inspired by mattn/go-sqlite3: + // https://github.com/mattn/go-sqlite3/blob/f76bae4b0044cbba8fb2c72b8e4559e8fbcffd86/sqlite3.go#L2254-L2262 + // but we put make this compatibility optional behind a DSN + // query parameter, because this changes API behavior, so an + // opt-in is needed. + switch r.ColumnTypeDatabaseTypeName(i) { + case "DATE", "DATETIME", "TIMESTAMP": + // Is it a seconds timestamp or a milliseconds + // timestamp? + if v > 1e12 || v < -1e12 { + // time.Unix expects nanoseconds, but this is a + // milliseconds timestamp, so convert ms->ns. + v *= int64(time.Millisecond) + dest[i] = time.Unix(0, v).UTC() + } else { + dest[i] = time.Unix(v, 0) + } + default: + dest[i] = v + } + } + case sqlite3.SQLITE_FLOAT: + v, err := r.c.columnDouble(r.pstmt, i) + if err != nil { + return err + } + + dest[i] = v + case sqlite3.SQLITE_TEXT: + v, err := r.c.columnText(r.pstmt, i) + if err != nil { + return err + } + + switch r.ColumnTypeDatabaseTypeName(i) { + case "DATE", "DATETIME", "TIMESTAMP": + dest[i], _ = r.c.parseTime(v) + default: + dest[i] = v + } + case sqlite3.SQLITE_BLOB: + v, err := r.c.columnBlob(r.pstmt, i) + if err != nil { + return err + } + + dest[i] = v + case sqlite3.SQLITE_NULL: + dest[i] = nil + default: + return fmt.Errorf("internal error: rc %d", rc) + } + } + return nil + case sqlite3.SQLITE_DONE: + return io.EOF + default: + return r.c.errstr(int32(rc)) + } +} + +// RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return +// the database system type name without the length. Type names should be +// uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", +// "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", +// "JSONB", "XML", "TIMESTAMP". +func (r *rows) ColumnTypeDatabaseTypeName(index int) string { + return strings.ToUpper(r.c.columnDeclType(r.pstmt, index)) +} + +// RowsColumnTypeLength may be implemented by Rows. It should return the length +// of the column type if the column is a variable length type. If the column is +// not a variable length type ok should return false. If length is not limited +// other than system limits, it should return math.MaxInt64. The following are +// examples of returned values for various types: +// +// TEXT (math.MaxInt64, true) +// varchar(10) (10, true) +// nvarchar(10) (10, true) +// decimal (0, false) +// int (0, false) +// bytea(30) (30, true) +func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) { + t, err := r.c.columnType(r.pstmt, index) + if err != nil { + return 0, false + } + + switch t { + case sqlite3.SQLITE_INTEGER: + return 0, false + case sqlite3.SQLITE_FLOAT: + return 0, false + case sqlite3.SQLITE_TEXT: + return math.MaxInt64, true + case sqlite3.SQLITE_BLOB: + return math.MaxInt64, true + case sqlite3.SQLITE_NULL: + return 0, false + default: + return 0, false + } +} + +// RowsColumnTypeNullable may be implemented by Rows. The nullable value should +// be true if it is known the column may be null, or false if the column is +// known to be not nullable. If the column nullability is unknown, ok should be +// false. +func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) { + return true, true +} + +// RowsColumnTypePrecisionScale may be implemented by Rows. It should return +// the precision and scale for decimal types. If not applicable, ok should be +// false. The following are examples of returned values for various types: +// +// decimal(38, 4) (38, 4, true) +// int (0, 0, false) +// decimal (math.MaxInt64, math.MaxInt64, true) +func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { + return 0, 0, false +} + +// RowsColumnTypeScanType may be implemented by Rows. It should return the +// value type that can be used to scan types into. For example, the database +// column type "bigint" this should return "reflect.TypeOf(int64(0))". +func (r *rows) ColumnTypeScanType(index int) reflect.Type { + t, err := r.c.columnType(r.pstmt, index) + if err != nil { + return reflect.TypeOf("") + } + + switch t { + case sqlite3.SQLITE_INTEGER: + switch strings.ToLower(r.c.columnDeclType(r.pstmt, index)) { + case "boolean": + return reflect.TypeOf(false) + case "date", "datetime", "time", "timestamp": + return reflect.TypeOf(time.Time{}) + default: + return reflect.TypeOf(int64(0)) + } + case sqlite3.SQLITE_FLOAT: + return reflect.TypeOf(float64(0)) + case sqlite3.SQLITE_TEXT: + return reflect.TypeOf("") + case sqlite3.SQLITE_BLOB: + return reflect.TypeOf([]byte(nil)) + case sqlite3.SQLITE_NULL: + return reflect.TypeOf(nil) + default: + return reflect.TypeOf("") + } +} + +// C documentation +// +// int sqlite3_reset(sqlite3_stmt *pStmt); +func (c *conn) reset(pstmt uintptr) error { + if rc := sqlite3.Xsqlite3_reset(c.tls, pstmt); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + return nil +} diff --git a/vendor/modernc.org/sqlite/sqlite.go b/vendor/modernc.org/sqlite/sqlite.go index 01e6a03..d175293 100644 --- a/vendor/modernc.org/sqlite/sqlite.go +++ b/vendor/modernc.org/sqlite/sqlite.go @@ -12,11 +12,8 @@ import ( "database/sql/driver" "errors" "fmt" - "io" - "math" "math/bits" "net/url" - "reflect" "runtime" "sort" "strconv" @@ -56,267 +53,10 @@ const ( sqliteLockedSharedcache = sqlite3.SQLITE_LOCKED | (1 << 8) ) -// https://gitlab.com/cznic/sqlite/-/issues/199 -func init() { - sqlite3.PatchIssue199() -} - -// Error represents sqlite library error code. -type Error struct { - msg string - code int -} - -// Error implements error. -func (e *Error) Error() string { return e.msg } - -// Code returns the sqlite result code for this error. -func (e *Error) Code() int { return e.code } - -var ( - // ErrorCodeString maps Error.Code() to its string representation. - ErrorCodeString = map[int]string{ - sqlite3.SQLITE_ABORT: "Callback routine requested an abort (SQLITE_ABORT)", - sqlite3.SQLITE_AUTH: "Authorization denied (SQLITE_AUTH)", - sqlite3.SQLITE_BUSY: "The database file is locked (SQLITE_BUSY)", - sqlite3.SQLITE_CANTOPEN: "Unable to open the database file (SQLITE_CANTOPEN)", - sqlite3.SQLITE_CONSTRAINT: "Abort due to constraint violation (SQLITE_CONSTRAINT)", - sqlite3.SQLITE_CORRUPT: "The database disk image is malformed (SQLITE_CORRUPT)", - sqlite3.SQLITE_DONE: "sqlite3_step() has finished executing (SQLITE_DONE)", - sqlite3.SQLITE_EMPTY: "Internal use only (SQLITE_EMPTY)", - sqlite3.SQLITE_ERROR: "Generic error (SQLITE_ERROR)", - sqlite3.SQLITE_FORMAT: "Not used (SQLITE_FORMAT)", - sqlite3.SQLITE_FULL: "Insertion failed because database is full (SQLITE_FULL)", - sqlite3.SQLITE_INTERNAL: "Internal logic error in SQLite (SQLITE_INTERNAL)", - sqlite3.SQLITE_INTERRUPT: "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)", - sqlite3.SQLITE_IOERR | (1 << 8): "(SQLITE_IOERR_READ)", - sqlite3.SQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)", - sqlite3.SQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)", - sqlite3.SQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)", - sqlite3.SQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)", - sqlite3.SQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)", - sqlite3.SQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)", - sqlite3.SQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)", - sqlite3.SQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)", - sqlite3.SQLITE_IOERR | (2 << 8): "(SQLITE_IOERR_SHORT_READ)", - sqlite3.SQLITE_IOERR | (3 << 8): "(SQLITE_IOERR_WRITE)", - sqlite3.SQLITE_IOERR | (4 << 8): "(SQLITE_IOERR_FSYNC)", - sqlite3.SQLITE_IOERR | (5 << 8): "(SQLITE_IOERR_DIR_FSYNC)", - sqlite3.SQLITE_IOERR | (6 << 8): "(SQLITE_IOERR_TRUNCATE)", - sqlite3.SQLITE_IOERR | (7 << 8): "(SQLITE_IOERR_FSTAT)", - sqlite3.SQLITE_IOERR | (8 << 8): "(SQLITE_IOERR_UNLOCK)", - sqlite3.SQLITE_IOERR | (9 << 8): "(SQLITE_IOERR_RDLOCK)", - sqlite3.SQLITE_IOERR: "Some kind of disk I/O error occurred (SQLITE_IOERR)", - sqlite3.SQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)", - sqlite3.SQLITE_LOCKED: "A table in the database is locked (SQLITE_LOCKED)", - sqlite3.SQLITE_MISMATCH: "Data type mismatch (SQLITE_MISMATCH)", - sqlite3.SQLITE_MISUSE: "Library used incorrectly (SQLITE_MISUSE)", - sqlite3.SQLITE_NOLFS: "Uses OS features not supported on host (SQLITE_NOLFS)", - sqlite3.SQLITE_NOMEM: "A malloc() failed (SQLITE_NOMEM)", - sqlite3.SQLITE_NOTADB: "File opened that is not a database file (SQLITE_NOTADB)", - sqlite3.SQLITE_NOTFOUND: "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)", - sqlite3.SQLITE_NOTICE: "Notifications from sqlite3_log() (SQLITE_NOTICE)", - sqlite3.SQLITE_PERM: "Access permission denied (SQLITE_PERM)", - sqlite3.SQLITE_PROTOCOL: "Database lock protocol error (SQLITE_PROTOCOL)", - sqlite3.SQLITE_RANGE: "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)", - sqlite3.SQLITE_READONLY: "Attempt to write a readonly database (SQLITE_READONLY)", - sqlite3.SQLITE_ROW: "sqlite3_step() has another row ready (SQLITE_ROW)", - sqlite3.SQLITE_SCHEMA: "The database schema changed (SQLITE_SCHEMA)", - sqlite3.SQLITE_TOOBIG: "String or BLOB exceeds size limit (SQLITE_TOOBIG)", - sqlite3.SQLITE_WARNING: "Warnings from sqlite3_log() (SQLITE_WARNING)", - } -) - func init() { sql.Register(driverName, newDriver()) -} + sqlite3.PatchIssue199() // https://gitlab.com/cznic/sqlite/-/issues/199 -type result struct { - lastInsertID int64 - rowsAffected int -} - -func newResult(c *conn) (_ *result, err error) { - r := &result{} - if r.rowsAffected, err = c.changes(); err != nil { - return nil, err - } - - if r.lastInsertID, err = c.lastInsertRowID(); err != nil { - return nil, err - } - - return r, nil -} - -// LastInsertId returns the database's auto-generated ID after, for example, an -// INSERT into a table with primary key. -func (r *result) LastInsertId() (int64, error) { - if r == nil { - return 0, nil - } - - return r.lastInsertID, nil -} - -// RowsAffected returns the number of rows affected by the query. -func (r *result) RowsAffected() (int64, error) { - if r == nil { - return 0, nil - } - - return int64(r.rowsAffected), nil -} - -type rows struct { - allocs []uintptr - c *conn - columns []string - pstmt uintptr - - doStep bool - empty bool -} - -func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err error) { - r = &rows{c: c, pstmt: pstmt, allocs: allocs, empty: empty} - - defer func() { - if err != nil { - r.Close() - r = nil - } - }() - - n, err := c.columnCount(pstmt) - if err != nil { - return nil, err - } - - r.columns = make([]string, n) - for i := range r.columns { - if r.columns[i], err = r.c.columnName(pstmt, i); err != nil { - return nil, err - } - } - - return r, nil -} - -// Close closes the rows iterator. -func (r *rows) Close() (err error) { - for _, v := range r.allocs { - r.c.free(v) - } - r.allocs = nil - return r.c.finalize(r.pstmt) -} - -// Columns returns the names of the columns. The number of columns of the -// result is inferred from the length of the slice. If a particular column name -// isn't known, an empty string should be returned for that entry. -func (r *rows) Columns() (c []string) { - return r.columns -} - -// Next is called to populate the next row of data into the provided slice. The -// provided slice will be the same size as the Columns() are wide. -// -// Next should return io.EOF when there are no more rows. -func (r *rows) Next(dest []driver.Value) (err error) { - if r.empty { - return io.EOF - } - - rc := sqlite3.SQLITE_ROW - if r.doStep { - if rc, err = r.c.step(r.pstmt); err != nil { - return err - } - } - - r.doStep = true - switch rc { - case sqlite3.SQLITE_ROW: - if g, e := len(dest), len(r.columns); g != e { - return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e) - } - - for i := range dest { - ct, err := r.c.columnType(r.pstmt, i) - if err != nil { - return err - } - - switch ct { - case sqlite3.SQLITE_INTEGER: - v, err := r.c.columnInt64(r.pstmt, i) - if err != nil { - return err - } - - if !r.c.intToTime { - dest[i] = v - } else { - // Inspired by mattn/go-sqlite3: - // https://github.com/mattn/go-sqlite3/blob/f76bae4b0044cbba8fb2c72b8e4559e8fbcffd86/sqlite3.go#L2254-L2262 - // but we put make this compatibility optional behind a DSN - // query parameter, because this changes API behavior, so an - // opt-in is needed. - switch r.ColumnTypeDatabaseTypeName(i) { - case "DATE", "DATETIME", "TIMESTAMP": - // Is it a seconds timestamp or a milliseconds - // timestamp? - if v > 1e12 || v < -1e12 { - // time.Unix expects nanoseconds, but this is a - // milliseconds timestamp, so convert ms->ns. - v *= int64(time.Millisecond) - dest[i] = time.Unix(0, v).UTC() - } else { - dest[i] = time.Unix(v, 0) - } - default: - dest[i] = v - } - } - case sqlite3.SQLITE_FLOAT: - v, err := r.c.columnDouble(r.pstmt, i) - if err != nil { - return err - } - - dest[i] = v - case sqlite3.SQLITE_TEXT: - v, err := r.c.columnText(r.pstmt, i) - if err != nil { - return err - } - - switch r.ColumnTypeDatabaseTypeName(i) { - case "DATE", "DATETIME", "TIMESTAMP": - dest[i], _ = r.c.parseTime(v) - default: - dest[i] = v - } - case sqlite3.SQLITE_BLOB: - v, err := r.c.columnBlob(r.pstmt, i) - if err != nil { - return err - } - - dest[i] = v - case sqlite3.SQLITE_NULL: - dest[i] = nil - default: - return fmt.Errorf("internal error: rc %d", rc) - } - } - return nil - case sqlite3.SQLITE_DONE: - return io.EOF - default: - return r.c.errstr(int32(rc)) - } } // Inspired by mattn/go-sqlite3: https://github.com/mattn/go-sqlite3/blob/ab91e934/sqlite3.go#L210-L226 @@ -332,472 +72,6 @@ var parseTimeFormats = []string{ "2006-01-02", } -// Attempt to parse s as a time. Return (s, false) if s is not -// recognized as a valid time encoding. -func (c *conn) parseTime(s string) (interface{}, bool) { - if v, ok := c.parseTimeString(s, strings.Index(s, "m=")); ok { - return v, true - } - - ts := strings.TrimSuffix(s, "Z") - - for _, f := range parseTimeFormats { - t, err := time.Parse(f, ts) - if err == nil { - return t, true - } - } - - return s, false -} - -// Attempt to parse s as a time string produced by t.String(). If x > 0 it's -// the index of substring "m=" within s. Return (s, false) if s is -// not recognized as a valid time encoding. -func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) { - s := s0 - if x > 0 { - s = s[:x] // "2006-01-02 15:04:05.999999999 -0700 MST m=+9999" -> "2006-01-02 15:04:05.999999999 -0700 MST " - } - s = strings.TrimSpace(s) - if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil { - return t, true - } - - return s0, false -} - -// writeTimeFormats are the names and formats supported -// by the `_time_format` DSN query param. -var writeTimeFormats = map[string]string{ - "sqlite": parseTimeFormats[0], -} - -func (c *conn) formatTime(t time.Time) string { - // Before configurable write time formats were supported, - // time.Time.String was used. Maintain that default to - // keep existing driver users formatting times the same. - if c.writeTimeFormat == "" { - return t.String() - } - return t.Format(c.writeTimeFormat) -} - -// RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return -// the database system type name without the length. Type names should be -// uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", -// "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", -// "JSONB", "XML", "TIMESTAMP". -func (r *rows) ColumnTypeDatabaseTypeName(index int) string { - return strings.ToUpper(r.c.columnDeclType(r.pstmt, index)) -} - -// RowsColumnTypeLength may be implemented by Rows. It should return the length -// of the column type if the column is a variable length type. If the column is -// not a variable length type ok should return false. If length is not limited -// other than system limits, it should return math.MaxInt64. The following are -// examples of returned values for various types: -// -// TEXT (math.MaxInt64, true) -// varchar(10) (10, true) -// nvarchar(10) (10, true) -// decimal (0, false) -// int (0, false) -// bytea(30) (30, true) -func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) { - t, err := r.c.columnType(r.pstmt, index) - if err != nil { - return 0, false - } - - switch t { - case sqlite3.SQLITE_INTEGER: - return 0, false - case sqlite3.SQLITE_FLOAT: - return 0, false - case sqlite3.SQLITE_TEXT: - return math.MaxInt64, true - case sqlite3.SQLITE_BLOB: - return math.MaxInt64, true - case sqlite3.SQLITE_NULL: - return 0, false - default: - return 0, false - } -} - -// RowsColumnTypeNullable may be implemented by Rows. The nullable value should -// be true if it is known the column may be null, or false if the column is -// known to be not nullable. If the column nullability is unknown, ok should be -// false. -func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) { - return true, true -} - -// RowsColumnTypePrecisionScale may be implemented by Rows. It should return -// the precision and scale for decimal types. If not applicable, ok should be -// false. The following are examples of returned values for various types: -// -// decimal(38, 4) (38, 4, true) -// int (0, 0, false) -// decimal (math.MaxInt64, math.MaxInt64, true) -func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { - return 0, 0, false -} - -// RowsColumnTypeScanType may be implemented by Rows. It should return the -// value type that can be used to scan types into. For example, the database -// column type "bigint" this should return "reflect.TypeOf(int64(0))". -func (r *rows) ColumnTypeScanType(index int) reflect.Type { - t, err := r.c.columnType(r.pstmt, index) - if err != nil { - return reflect.TypeOf("") - } - - switch t { - case sqlite3.SQLITE_INTEGER: - switch strings.ToLower(r.c.columnDeclType(r.pstmt, index)) { - case "boolean": - return reflect.TypeOf(false) - case "date", "datetime", "time", "timestamp": - return reflect.TypeOf(time.Time{}) - default: - return reflect.TypeOf(int64(0)) - } - case sqlite3.SQLITE_FLOAT: - return reflect.TypeOf(float64(0)) - case sqlite3.SQLITE_TEXT: - return reflect.TypeOf("") - case sqlite3.SQLITE_BLOB: - return reflect.TypeOf([]byte(nil)) - case sqlite3.SQLITE_NULL: - return reflect.TypeOf(nil) - default: - return reflect.TypeOf("") - } -} - -type stmt struct { - c *conn - psql uintptr -} - -func newStmt(c *conn, sql string) (*stmt, error) { - p, err := libc.CString(sql) - if err != nil { - return nil, err - } - stm := stmt{c: c, psql: p} - - return &stm, nil -} - -// Close closes the statement. -// -// As of Go 1.1, a Stmt will not be closed if it's in use by any queries. -func (s *stmt) Close() (err error) { - s.c.free(s.psql) - s.psql = 0 - return nil -} - -// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE. -// -// Deprecated: Drivers should implement StmtExecContext instead (or -// additionally). -func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { //TODO StmtExecContext - return s.exec(context.Background(), toNamedValues(args)) -} - -// toNamedValues converts []driver.Value to []driver.NamedValue -func toNamedValues(vals []driver.Value) (r []driver.NamedValue) { - r = make([]driver.NamedValue, len(vals)) - for i, val := range vals { - r[i] = driver.NamedValue{Value: val, Ordinal: i + 1} - } - return r -} - -func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { - var pstmt uintptr - var done int32 - if ctx != nil { - if ctxDone := ctx.Done(); ctxDone != nil { - select { - case <-ctxDone: - return nil, ctx.Err() - default: - } - defer interruptOnDone(ctx, s.c, &done)() - } - } - - defer func() { - if ctx != nil && atomic.LoadInt32(&done) != 0 { - r, err = nil, ctx.Err() - } - if pstmt != 0 { - // ensure stmt finalized. - e := s.c.finalize(pstmt) - - if err == nil && e != nil { - // prioritize original - // returned error. - err = e - } - } - }() - - for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { - if pstmt, err = s.c.prepareV2(&psql); err != nil { - return nil, err - } - - if pstmt == 0 { - continue - } - err = func() (err error) { - n, err := s.c.bindParameterCount(pstmt) - if err != nil { - return err - } - - if n != 0 { - allocs, err := s.c.bind(pstmt, n, args) - if err != nil { - return err - } - - if len(allocs) != 0 { - defer func() { - for _, v := range allocs { - s.c.free(v) - } - }() - } - } - - rc, err := s.c.step(pstmt) - if err != nil { - return err - } - - switch rc & 0xff { - case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: - r, err = newResult(s.c) - default: - return s.c.errstr(int32(rc)) - } - - return nil - }() - - e := s.c.finalize(pstmt) - pstmt = 0 // done with - - if err == nil && e != nil { - // prioritize original - // returned error. - err = e - } - - if err != nil { - return nil, err - } - } - return r, err -} - -// NumInput returns the number of placeholder parameters. -// -// If NumInput returns >= 0, the sql package will sanity check argument counts -// from callers and return errors to the caller before the statement's Exec or -// Query methods are called. -// -// NumInput may also return -1, if the driver doesn't know its number of -// placeholders. In that case, the sql package will not sanity check Exec or -// Query argument counts. -func (s *stmt) NumInput() (n int) { - return -1 -} - -// Query executes a query that may return rows, such as a -// SELECT. -// -// Deprecated: Drivers should implement StmtQueryContext instead (or -// additionally). -func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { //TODO StmtQueryContext - return s.query(context.Background(), toNamedValues(args)) -} - -func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { - var pstmt uintptr - var done int32 - if ctx != nil { - if ctxDone := ctx.Done(); ctxDone != nil { - select { - case <-ctxDone: - return nil, ctx.Err() - default: - } - defer interruptOnDone(ctx, s.c, &done)() - } - } - - var allocs []uintptr - - defer func() { - if ctx != nil && atomic.LoadInt32(&done) != 0 { - if r != nil { - r.Close() - } - r, err = nil, ctx.Err() - } else if r == nil && err == nil { - r, err = newRows(s.c, pstmt, allocs, true) - } - - if pstmt != 0 { - // ensure stmt finalized. - e := s.c.finalize(pstmt) - - if err == nil && e != nil { - // prioritize original - // returned error. - err = e - } - } - - }() - - for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { - if pstmt, err = s.c.prepareV2(&psql); err != nil { - return nil, err - } - - if pstmt == 0 { - continue - } - - err = func() (err error) { - n, err := s.c.bindParameterCount(pstmt) - if err != nil { - return err - } - - if n != 0 { - if allocs, err = s.c.bind(pstmt, n, args); err != nil { - return err - } - } - - rc, err := s.c.step(pstmt) - if err != nil { - return err - } - - switch rc & 0xff { - case sqlite3.SQLITE_ROW: - if r != nil { - r.Close() - } - if r, err = newRows(s.c, pstmt, allocs, false); err != nil { - return err - } - - pstmt = 0 - return nil - case sqlite3.SQLITE_DONE: - if r == nil { - if r, err = newRows(s.c, pstmt, allocs, true); err != nil { - return err - } - pstmt = 0 - return nil - } - - // nop - default: - return s.c.errstr(int32(rc)) - } - - if *(*byte)(unsafe.Pointer(psql)) == 0 { - if r != nil { - r.Close() - } - if r, err = newRows(s.c, pstmt, allocs, true); err != nil { - return err - } - - pstmt = 0 - } - return nil - }() - - e := s.c.finalize(pstmt) - pstmt = 0 // done with - - if err == nil && e != nil { - // prioritize original - // returned error. - err = e - } - - if err != nil { - return nil, err - } - } - return r, err -} - -type tx struct { - c *conn -} - -func newTx(ctx context.Context, c *conn, opts driver.TxOptions) (*tx, error) { - r := &tx{c: c} - - sql := "begin" - if !opts.ReadOnly && c.beginMode != "" { - sql = "begin " + c.beginMode - } - - if err := r.exec(ctx, sql); err != nil { - return nil, err - } - - return r, nil -} - -// Commit implements driver.Tx. -func (t *tx) Commit() (err error) { - return t.exec(context.Background(), "commit") -} - -// Rollback implements driver.Tx. -func (t *tx) Rollback() (err error) { - return t.exec(context.Background(), "rollback") -} - -func (t *tx) exec(ctx context.Context, sql string) (err error) { - psql, err := libc.CString(sql) - if err != nil { - return err - } - - defer t.c.free(psql) - //TODO use t.conn.ExecContext() instead - - if ctx != nil && ctx.Done() != nil { - defer interruptOnDone(ctx, t.c, nil)() - } - - if rc := sqlite3.Xsqlite3_exec(t.c.tls, t.c.db, psql, 0, 0, 0); rc != sqlite3.SQLITE_OK { - return t.c.errstr(rc) - } - - return nil -} - // interruptOnDone sets up a goroutine to interrupt the provided db when the // context is canceled, and returns a function the caller must defer so it // doesn't interrupt after the caller finishes. @@ -835,66 +109,6 @@ func interruptOnDone( } } -type conn struct { - db uintptr // *sqlite3.Xsqlite3 - tls *libc.TLS - - // Context handling can cause conn.Close and conn.interrupt to be invoked - // concurrently. - sync.Mutex - - writeTimeFormat string - beginMode string - intToTime bool - integerTimeFormat string -} - -func newConn(dsn string) (*conn, error) { - var query, vfsName string - - // Parse the query parameters from the dsn and them from the dsn if not prefixed by file: - // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1046 - // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1383 - pos := strings.IndexRune(dsn, '?') - if pos >= 1 { - query = dsn[pos+1:] - var err error - vfsName, err = getVFSName(query) - if err != nil { - return nil, err - } - - if !strings.HasPrefix(dsn, "file:") { - dsn = dsn[:pos] - } - } - - c := &conn{tls: libc.NewTLS()} - db, err := c.openV2( - dsn, - vfsName, - sqlite3.SQLITE_OPEN_READWRITE|sqlite3.SQLITE_OPEN_CREATE| - sqlite3.SQLITE_OPEN_FULLMUTEX| - sqlite3.SQLITE_OPEN_URI, - ) - if err != nil { - return nil, err - } - - c.db = db - if err = c.extendedResultCodes(true); err != nil { - c.Close() - return nil, err - } - - if err = applyQueryParams(c, query); err != nil { - c.Close() - return nil, err - } - - return c, nil -} - func getVFSName(query string) (r string, err error) { q, err := url.ParseQuery(query) if err != nil { @@ -983,161 +197,6 @@ func applyQueryParams(c *conn, query string) error { return nil } -// C documentation -// -// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -func (c *conn) columnBlob(pstmt uintptr, iCol int) (v []byte, err error) { - p := sqlite3.Xsqlite3_column_blob(c.tls, pstmt, int32(iCol)) - len, err := c.columnBytes(pstmt, iCol) - if err != nil { - return nil, err - } - - if p == 0 || len == 0 { - return nil, nil - } - - v = make([]byte, len) - copy(v, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) - return v, nil -} - -// C documentation -// -// int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -func (c *conn) columnBytes(pstmt uintptr, iCol int) (_ int, err error) { - v := sqlite3.Xsqlite3_column_bytes(c.tls, pstmt, int32(iCol)) - return int(v), nil -} - -// C documentation -// -// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -func (c *conn) columnText(pstmt uintptr, iCol int) (v string, err error) { - p := sqlite3.Xsqlite3_column_text(c.tls, pstmt, int32(iCol)) - len, err := c.columnBytes(pstmt, iCol) - if err != nil { - return "", err - } - - if p == 0 || len == 0 { - return "", nil - } - - b := make([]byte, len) - copy(b, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) - return string(b), nil -} - -// C documentation -// -// double sqlite3_column_double(sqlite3_stmt*, int iCol); -func (c *conn) columnDouble(pstmt uintptr, iCol int) (v float64, err error) { - v = sqlite3.Xsqlite3_column_double(c.tls, pstmt, int32(iCol)) - return v, nil -} - -// C documentation -// -// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -func (c *conn) columnInt64(pstmt uintptr, iCol int) (v int64, err error) { - v = sqlite3.Xsqlite3_column_int64(c.tls, pstmt, int32(iCol)) - return v, nil -} - -// C documentation -// -// int sqlite3_column_type(sqlite3_stmt*, int iCol); -func (c *conn) columnType(pstmt uintptr, iCol int) (_ int, err error) { - v := sqlite3.Xsqlite3_column_type(c.tls, pstmt, int32(iCol)) - return int(v), nil -} - -// C documentation -// -// const char *sqlite3_column_decltype(sqlite3_stmt*,int); -func (c *conn) columnDeclType(pstmt uintptr, iCol int) string { - return libc.GoString(sqlite3.Xsqlite3_column_decltype(c.tls, pstmt, int32(iCol))) -} - -// C documentation -// -// const char *sqlite3_column_name(sqlite3_stmt*, int N); -func (c *conn) columnName(pstmt uintptr, n int) (string, error) { - p := sqlite3.Xsqlite3_column_name(c.tls, pstmt, int32(n)) - return libc.GoString(p), nil -} - -// C documentation -// -// int sqlite3_column_count(sqlite3_stmt *pStmt); -func (c *conn) columnCount(pstmt uintptr) (_ int, err error) { - v := sqlite3.Xsqlite3_column_count(c.tls, pstmt) - return int(v), nil -} - -// C documentation -// -// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); -func (c *conn) lastInsertRowID() (v int64, _ error) { - return sqlite3.Xsqlite3_last_insert_rowid(c.tls, c.db), nil -} - -// C documentation -// -// int sqlite3_changes(sqlite3*); -func (c *conn) changes() (int, error) { - v := sqlite3.Xsqlite3_changes(c.tls, c.db) - return int(v), nil -} - -// C documentation -// -// int sqlite3_step(sqlite3_stmt*); -func (c *conn) step(pstmt uintptr) (int, error) { - for { - switch rc := sqlite3.Xsqlite3_step(c.tls, pstmt); rc { - case sqliteLockedSharedcache: - if err := c.retry(pstmt); err != nil { - return sqlite3.SQLITE_LOCKED, err - } - case - sqlite3.SQLITE_DONE, - sqlite3.SQLITE_ROW: - - return int(rc), nil - default: - return int(rc), c.errstr(rc) - } - } -} - -func (c *conn) retry(pstmt uintptr) error { - mu := mutexAlloc(c.tls) - (*mutex)(unsafe.Pointer(mu)).Lock() - rc := sqlite3.Xsqlite3_unlock_notify( - c.tls, - c.db, - *(*uintptr)(unsafe.Pointer(&struct { - f func(*libc.TLS, uintptr, int32) - }{unlockNotify})), - mu, - ) - if rc == sqlite3.SQLITE_LOCKED { // Deadlock, see https://www.sqlite.org/c3ref/unlock_notify.html - (*mutex)(unsafe.Pointer(mu)).Unlock() - mutexFree(c.tls, mu) - return c.errstr(rc) - } - - (*mutex)(unsafe.Pointer(mu)).Lock() - (*mutex)(unsafe.Pointer(mu)).Unlock() - mutexFree(c.tls, mu) - if pstmt != 0 { - sqlite3.Xsqlite3_reset(c.tls, pstmt) - } - return nil -} - func unlockNotify(t *libc.TLS, ppArg uintptr, nArg int32) { for i := int32(0); i < nArg; i++ { mu := *(*uintptr)(unsafe.Pointer(ppArg)) @@ -1146,437 +205,6 @@ func unlockNotify(t *libc.TLS, ppArg uintptr, nArg int32) { } } -func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []uintptr, err error) { - defer func() { - if err == nil { - return - } - - for _, v := range allocs { - c.free(v) - } - allocs = nil - }() - - for i := 1; i <= n; i++ { - name, err := c.bindParameterName(pstmt, i) - if err != nil { - return allocs, err - } - - var found bool - var v driver.NamedValue - for _, v = range args { - if name != "" { - // For ?NNN and $NNN params, match if NNN == v.Ordinal. - // - // Supporting this for $NNN is a special case that makes eg - // `select $1, $2, $3 ...` work without needing to use - // sql.Named. - if (name[0] == '?' || name[0] == '$') && name[1:] == strconv.Itoa(v.Ordinal) { - found = true - break - } - - // sqlite supports '$', '@' and ':' prefixes for string - // identifiers and '?' for numeric, so we cannot - // combine different prefixes with the same name - // because `database/sql` requires variable names - // to start with a letter - if name[1:] == v.Name[:] { - found = true - break - } - } else { - if v.Ordinal == i { - found = true - break - } - } - } - - if !found { - if name != "" { - return allocs, fmt.Errorf("missing named argument %q", name[1:]) - } - - return allocs, fmt.Errorf("missing argument with index %d", i) - } - - var p uintptr - switch x := v.Value.(type) { - case int64: - if err := c.bindInt64(pstmt, i, x); err != nil { - return allocs, err - } - case float64: - if err := c.bindDouble(pstmt, i, x); err != nil { - return allocs, err - } - case bool: - v := 0 - if x { - v = 1 - } - if err := c.bindInt(pstmt, i, v); err != nil { - return allocs, err - } - case []byte: - if p, err = c.bindBlob(pstmt, i, x); err != nil { - return allocs, err - } - case string: - if p, err = c.bindText(pstmt, i, x); err != nil { - return allocs, err - } - case time.Time: - switch c.integerTimeFormat { - case "unix": - if err := c.bindInt64(pstmt, i, x.Unix()); err != nil { - return allocs, err - } - case "unix_milli": - if err := c.bindInt64(pstmt, i, x.UnixMilli()); err != nil { - return allocs, err - } - case "unix_micro": - if err := c.bindInt64(pstmt, i, x.UnixMicro()); err != nil { - return allocs, err - } - case "unix_nano": - if err := c.bindInt64(pstmt, i, x.UnixNano()); err != nil { - return allocs, err - } - default: - if p, err = c.bindText(pstmt, i, c.formatTime(x)); err != nil { - return allocs, err - } - } - - case nil: - if p, err = c.bindNull(pstmt, i); err != nil { - return allocs, err - } - default: - return allocs, fmt.Errorf("sqlite: invalid driver.Value type %T", x) - } - if p != 0 { - allocs = append(allocs, p) - } - } - return allocs, nil -} - -// C documentation -// -// int sqlite3_bind_null(sqlite3_stmt*, int); -func (c *conn) bindNull(pstmt uintptr, idx1 int) (uintptr, error) { - if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { - return 0, c.errstr(rc) - } - - return 0, nil -} - -// C documentation -// -// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); -func (c *conn) bindText(pstmt uintptr, idx1 int, value string) (uintptr, error) { - p, err := libc.CString(value) - if err != nil { - return 0, err - } - - if rc := sqlite3.Xsqlite3_bind_text(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { - c.free(p) - return 0, c.errstr(rc) - } - - return p, nil -} - -// C documentation -// -// int sqlite3_bind_int(sqlite3_stmt*, int, int); -func (c *conn) bindInt(pstmt uintptr, idx1, value int) (err error) { - if rc := sqlite3.Xsqlite3_bind_int(c.tls, pstmt, int32(idx1), int32(value)); rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// C documentation -// -// int sqlite3_bind_double(sqlite3_stmt*, int, double); -func (c *conn) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) { - if rc := sqlite3.Xsqlite3_bind_double(c.tls, pstmt, int32(idx1), value); rc != 0 { - return c.errstr(rc) - } - - return nil -} - -// C documentation -// -// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -func (c *conn) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) { - if rc := sqlite3.Xsqlite3_bind_int64(c.tls, pstmt, int32(idx1), value); rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// C documentation -// -// const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); -func (c *conn) bindParameterName(pstmt uintptr, i int) (string, error) { - p := sqlite3.Xsqlite3_bind_parameter_name(c.tls, pstmt, int32(i)) - return libc.GoString(p), nil -} - -// C documentation -// -// int sqlite3_bind_parameter_count(sqlite3_stmt*); -func (c *conn) bindParameterCount(pstmt uintptr) (_ int, err error) { - r := sqlite3.Xsqlite3_bind_parameter_count(c.tls, pstmt) - return int(r), nil -} - -// C documentation -// -// int sqlite3_finalize(sqlite3_stmt *pStmt); -func (c *conn) finalize(pstmt uintptr) error { - if rc := sqlite3.Xsqlite3_finalize(c.tls, pstmt); rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// C documentation -// -// int sqlite3_prepare_v2( -// sqlite3 *db, /* Database handle */ -// const char *zSql, /* SQL statement, UTF-8 encoded */ -// int nByte, /* Maximum length of zSql in bytes. */ -// sqlite3_stmt **ppStmt, /* OUT: Statement handle */ -// const char **pzTail /* OUT: Pointer to unused portion of zSql */ -// ); -func (c *conn) prepareV2(zSQL *uintptr) (pstmt uintptr, err error) { - var ppstmt, pptail uintptr - - defer func() { - c.free(ppstmt) - c.free(pptail) - }() - - if ppstmt, err = c.malloc(int(ptrSize)); err != nil { - return 0, err - } - - if pptail, err = c.malloc(int(ptrSize)); err != nil { - return 0, err - } - - for { - switch rc := sqlite3.Xsqlite3_prepare_v2(c.tls, c.db, *zSQL, -1, ppstmt, pptail); rc { - case sqlite3.SQLITE_OK: - *zSQL = *(*uintptr)(unsafe.Pointer(pptail)) - return *(*uintptr)(unsafe.Pointer(ppstmt)), nil - case sqliteLockedSharedcache: - if err := c.retry(0); err != nil { - return 0, err - } - default: - return 0, c.errstr(rc) - } - } -} - -// C documentation -// -// void sqlite3_interrupt(sqlite3*); -func (c *conn) interrupt(pdb uintptr) (err error) { - c.Lock() // Defend against race with .Close invoked by context handling. - - defer c.Unlock() - - if c.tls != nil { - sqlite3.Xsqlite3_interrupt(c.tls, pdb) - } - return nil -} - -// C documentation -// -// int sqlite3_extended_result_codes(sqlite3*, int onoff); -func (c *conn) extendedResultCodes(on bool) error { - if rc := sqlite3.Xsqlite3_extended_result_codes(c.tls, c.db, libc.Bool32(on)); rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// C documentation -// -// int sqlite3_open_v2( -// const char *filename, /* Database filename (UTF-8) */ -// sqlite3 **ppDb, /* OUT: SQLite db handle */ -// int flags, /* Flags */ -// const char *zVfs /* Name of VFS module to use */ -// ); -func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) { - var p, s, vfs uintptr - - defer func() { - if p != 0 { - c.free(p) - } - if s != 0 { - c.free(s) - } - if vfs != 0 { - c.free(vfs) - } - }() - - p, err := c.malloc(int(ptrSize)) - if err != nil { - return 0, err - } - - if s, err = libc.CString(name); err != nil { - return 0, err - } - - if vfsName != "" { - if vfs, err = libc.CString(vfsName); err != nil { - return 0, err - } - } - - if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK { - return 0, c.errstr(rc) - } - - return *(*uintptr)(unsafe.Pointer(p)), nil -} - -func (c *conn) malloc(n int) (uintptr, error) { - if p := libc.Xmalloc(c.tls, types.Size_t(n)); p != 0 || n == 0 { - return p, nil - } - - return 0, fmt.Errorf("sqlite: cannot allocate %d bytes of memory", n) -} - -func (c *conn) free(p uintptr) { - if p != 0 { - libc.Xfree(c.tls, p) - } -} - -// C documentation -// -// const char *sqlite3_errstr(int); -func (c *conn) errstr(rc int32) error { - p := sqlite3.Xsqlite3_errstr(c.tls, rc) - str := libc.GoString(p) - p = sqlite3.Xsqlite3_errmsg(c.tls, c.db) - var s string - if rc == sqlite3.SQLITE_BUSY { - s = " (SQLITE_BUSY)" - } - switch msg := libc.GoString(p); { - case msg == str: - return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)} - default: - return &Error{msg: fmt.Sprintf("%s: %s (%v)%s", str, msg, rc, s), code: int(rc)} - } -} - -// Begin starts a transaction. -// -// Deprecated: Drivers should implement ConnBeginTx instead (or additionally). -func (c *conn) Begin() (dt driver.Tx, err error) { - if dmesgs { - defer func() { - dmesg("conn %p: (driver.Tx %p, err %v)", c, dt, err) - }() - } - return c.begin(context.Background(), driver.TxOptions{}) -} - -func (c *conn) begin(ctx context.Context, opts driver.TxOptions) (t driver.Tx, err error) { - return newTx(ctx, c, opts) -} - -// Close invalidates and potentially stops any current prepared statements and -// transactions, marking this connection as no longer in use. -// -// Because the sql package maintains a free pool of connections and only calls -// Close when there's a surplus of idle connections, it shouldn't be necessary -// for drivers to do their own connection caching. -func (c *conn) Close() (err error) { - if dmesgs { - defer func() { - dmesg("conn %p: err %v", c, err) - }() - } - c.Lock() // Defend against race with .interrupt invoked by context handling. - - defer c.Unlock() - - if c.db != 0 { - if err := c.closeV2(c.db); err != nil { - return err - } - - c.db = 0 - } - - if c.tls != nil { - c.tls.Close() - c.tls = nil - } - return nil -} - -// C documentation -// -// int sqlite3_close_v2(sqlite3*); -func (c *conn) closeV2(db uintptr) error { - if rc := sqlite3.Xsqlite3_close_v2(c.tls, db); rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - - return nil -} - -// ResetSession is called prior to executing a query on the connection if the -// connection has been used before. If the driver returns ErrBadConn the -// connection is discarded. -func (c *conn) ResetSession(ctx context.Context) error { - if !c.usable() { - return driver.ErrBadConn - } - - return nil -} - -// IsValid is called prior to placing the connection into the connection pool. -// The connection will be discarded if false is returned. -func (c *conn) IsValid() bool { - return c.usable() -} - -func (c *conn) usable() bool { - return c.db != 0 && sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0 -} - // FunctionImpl describes an [application-defined SQL function]. If Scalar is // set, it is treated as a scalar function; otherwise, it is treated as an // aggregate function using MakeAggregate. @@ -1635,53 +263,6 @@ type AggregateFunction interface { Final(ctx *FunctionContext) } -type userDefinedFunction struct { - zFuncName uintptr - nArg int32 - eTextRep int32 - pApp uintptr - - scalar bool - freeOnce sync.Once -} - -func (c *conn) createFunctionInternal(fun *userDefinedFunction) error { - var rc int32 - - if fun.scalar { - rc = sqlite3.Xsqlite3_create_function( - c.tls, - c.db, - fun.zFuncName, - fun.nArg, - fun.eTextRep, - fun.pApp, - cFuncPointer(funcTrampoline), - 0, - 0, - ) - } else { - rc = sqlite3.Xsqlite3_create_window_function( - c.tls, - c.db, - fun.zFuncName, - fun.nArg, - fun.eTextRep, - fun.pApp, - cFuncPointer(stepTrampoline), - cFuncPointer(finalTrampoline), - cFuncPointer(valueTrampoline), - cFuncPointer(inverseTrampoline), - 0, - ) - } - - if rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - return nil -} - type collation struct { zName uintptr pApp uintptr @@ -1751,241 +332,6 @@ func registerCollation( return nil } -func (c *conn) createCollationInternal(coll *collation) error { - rc := sqlite3.Xsqlite3_create_collation_v2( - c.tls, - c.db, - coll.zName, - coll.enc, - coll.pApp, - cFuncPointer(collationTrampoline), - 0, - ) - if rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - return nil -} - -// Execer is an optional interface that may be implemented by a Conn. -// -// If a Conn does not implement Execer, the sql package's DB.Exec will first -// prepare a query, execute the statement, and then close the statement. -// -// Exec may return ErrSkip. -// -// Deprecated: Drivers should implement ExecerContext instead. -func (c *conn) Exec(query string, args []driver.Value) (dr driver.Result, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, query %q, args %v: (driver.Result %p, err %v)", c, query, args, dr, err) - }() - } - return c.exec(context.Background(), query, toNamedValues(args)) -} - -func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { - s, err := c.prepare(ctx, query) - if err != nil { - return nil, err - } - - defer func() { - if err2 := s.Close(); err2 != nil && err == nil { - err = err2 - } - }() - - return s.(*stmt).exec(ctx, args) -} - -// Prepare returns a prepared statement, bound to this connection. -func (c *conn) Prepare(query string) (ds driver.Stmt, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, query %q: (driver.Stmt %p, err %v)", c, query, ds, err) - }() - } - return c.prepare(context.Background(), query) -} - -func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { - //TODO use ctx - return newStmt(c, query) -} - -// Queryer is an optional interface that may be implemented by a Conn. -// -// If a Conn does not implement Queryer, the sql package's DB.Query will first -// prepare a query, execute the statement, and then close the statement. -// -// Query may return ErrSkip. -// -// Deprecated: Drivers should implement QueryerContext instead. -func (c *conn) Query(query string, args []driver.Value) (dr driver.Rows, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, query %q, args %v: (driver.Rows %p, err %v)", c, query, args, dr, err) - }() - } - return c.query(context.Background(), query, toNamedValues(args)) -} - -func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { - s, err := c.prepare(ctx, query) - if err != nil { - return nil, err - } - - defer func() { - if err2 := s.Close(); err2 != nil && err == nil { - err = err2 - } - }() - - return s.(*stmt).query(ctx, args) -} - -// Serialize returns a serialization of the main database. For an ordinary on-disk -// database file, the serialization is just a copy of the disk file. For an in-memory -// database or a "TEMP" database, the serialization is the same sequence of bytes -// which would be written to disk if that database where backed up to disk. -func (c *conn) Serialize() (v []byte, err error) { - pLen := c.tls.Alloc(8) - defer c.tls.Free(8) - - zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) - if zSchema == 0 { - return nil, fmt.Errorf("failed to get main db name") - } - - pBuf := sqlite3.Xsqlite3_serialize(c.tls, c.db, zSchema, pLen, 0) - bufLen := *(*sqlite3.Sqlite3_int64)(unsafe.Pointer(pLen)) - if pBuf != 0 { - defer sqlite3.Xsqlite3_free(c.tls, pBuf) - } - if bufLen <= 0 { - return nil, fmt.Errorf("invalid length returned: %d", bufLen) - } else if pBuf == 0 || bufLen == 0 { - return nil, nil - } - - v = make([]byte, bufLen) - copy(v, (*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen]) - return v, nil -} - -// Deserialize restore a database from the content returned by Serialize. -func (c *conn) Deserialize(buf []byte) (err error) { - bufLen := len(buf) - pBuf := c.tls.Alloc(bufLen) // free will be done if it fails or on close, must not be freed here - - copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf) - - zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) - if zSchema == 0 { - return fmt.Errorf("failed to get main db name") - } - - rc := sqlite3.Xsqlite3_deserialize(c.tls, c.db, zSchema, pBuf, int64(bufLen), int64(bufLen), sqlite3.SQLITE_DESERIALIZE_RESIZEABLE|sqlite3.SQLITE_DESERIALIZE_FREEONCLOSE) - if rc != sqlite3.SQLITE_OK { - return c.errstr(rc) - } - return nil -} - -// Backup object is used to manage progress and cleanup an online backup. It -// is returned by NewBackup or NewRestore. -type Backup struct { - srcConn *conn // source database connection - dstConn *conn // destination database connection - pBackup uintptr // sqlite3_backup object pointer -} - -// NewBackup returns a Backup object that will create an online backup of -// current database to the databased pointed by the passed URI. -func (c *conn) NewBackup(dstUri string) (*Backup, error) { - dstConn, err := newConn(dstUri) - if err != nil { - return nil, err - } - backup, err := c.backup(dstConn, false) - if err != nil { - dstConn.Close() - } - return backup, err -} - -// NewRestore returns a Backup object that will restore a backup to current -// database from the databased pointed by the passed URI. -func (c *conn) NewRestore(srcUri string) (*Backup, error) { - srcConn, err := newConn(srcUri) - if err != nil { - return nil, err - } - backup, err := c.backup(srcConn, true) - if err != nil { - srcConn.Close() - } - return backup, err -} - -func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error) { - srcSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) - if srcSchema == 0 { - return nil, fmt.Errorf("failed to get main source db name") - } - - dstSchema := sqlite3.Xsqlite3_db_name(remoteConn.tls, remoteConn.db, 0) - if dstSchema == 0 { - return nil, fmt.Errorf("failed to get main destination db name") - } - - var pBackup uintptr - if restore { - pBackup = sqlite3.Xsqlite3_backup_init(c.tls, c.db, srcSchema, remoteConn.db, dstSchema) - } else { - pBackup = sqlite3.Xsqlite3_backup_init(c.tls, remoteConn.db, dstSchema, c.db, srcSchema) - } - if pBackup <= 0 { - rc := sqlite3.Xsqlite3_errcode(c.tls, remoteConn.db) - return nil, c.errstr(rc) - } - - return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil -} - -// Step will copy up to n pages between the source and destination databases -// specified by the backup object. If n is negative, all remaining source -// pages are copied. -// If it successfully copies n pages and there are still more pages to be -// copied, then the function returns true with no error. If it successfully -// finishes copying all pages from source to destination, then it returns -// false with no error. If an error occurs while running, then an error is -// returned. -func (b *Backup) Step(n int32) (bool, error) { - rc := sqlite3.Xsqlite3_backup_step(b.srcConn.tls, b.pBackup, n) - if rc == sqlite3.SQLITE_OK { - return true, nil - } else if rc == sqlite3.SQLITE_DONE { - return false, nil - } else { - return false, b.srcConn.errstr(rc) - } -} - -// Finish releases all resources associated with the Backup object. The Backup -// object is invalid and may not be used following a call to Finish. -func (b *Backup) Finish() error { - rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup) - b.dstConn.Close() - if rc == sqlite3.SQLITE_OK { - return nil - } else { - return b.srcConn.errstr(rc) - } -} - type ExecQuerierContext interface { driver.ExecerContext driver.QueryerContext @@ -1997,21 +343,6 @@ type HookRegisterer interface { RegisterRollbackHook(RollbackHookFn) } -// Commit releases all resources associated with the Backup object but does not -// close the destination database connection. -// -// The destination database connection is returned to the caller or an error if raised. -// It is the responsibility of the caller to handle the connection closure. -func (b *Backup) Commit() (driver.Conn, error) { - rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup) - - if rc == sqlite3.SQLITE_OK { - return b.dstConn, nil - } else { - return nil, b.srcConn.errstr(rc) - } -} - // ConnectionHookFn function type for a connection hook on the Driver. Connection // hooks are called after the connection has been set up. type ConnectionHookFn func( @@ -2019,96 +350,6 @@ type ConnectionHookFn func( dsn string, ) error -// Driver implements database/sql/driver.Driver. -type Driver struct { - // user defined functions that are added to every new connection on Open - udfs map[string]*userDefinedFunction - // collations that are added to every new connection on Open - collations map[string]*collation - // connection hooks are called after a connection is opened - connectionHooks []ConnectionHookFn -} - -var d = &Driver{ - udfs: make(map[string]*userDefinedFunction, 0), - collations: make(map[string]*collation, 0), - connectionHooks: make([]ConnectionHookFn, 0), -} - -func newDriver() *Driver { return d } - -// Open returns a new connection to the database. The name is a string in a -// driver-specific format. -// -// Open may return a cached connection (one previously closed), but doing so is -// unnecessary; the sql package maintains a pool of idle connections for -// efficient re-use. -// -// The returned connection is only used by one goroutine at a time. -// -// The name may be a filename, e.g., "/tmp/mydata.sqlite", or a URI, in which -// case it may include a '?' followed by one or more query parameters. -// For example, "file:///tmp/mydata.sqlite?_pragma=foreign_keys(1)&_time_format=sqlite". -// The supported query parameters are: -// -// _pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA -// keyword added for you). May be specified more than once, '&'-separated. For more -// information on supported PRAGMAs see: https://www.sqlite.org/pragma.html -// -// _time_format: The name of a format to use when writing time values to the -// database. Currently the only supported value is "sqlite", which corresponds -// to format 7 from https://www.sqlite.org/lang_datefunc.html#time_values, -// including the timezone specifier. If this parameter is not specified, then -// the default String() format will be used. -// -// _time_integer_format: The name of a integer format to use when writing time values. -// By default, the time is stored as string and the format can be set with _time_format -// parameter. If _time_integer_format is set, the time will be stored as an integer and -// the integer value will depend on the integer format. -// If you decide to set both _time_format and _time_integer_format, the time will be -// converted as integer and the _time_format value will be ignored. -// Currently the supported value are "unix","unix_milli", "unix_micro" and "unix_nano", -// which corresponds to seconds, milliseconds, microseconds or nanoseconds -// since unixepoch (1 January 1970 00:00:00 UTC). -// -// _inttotime: Enable conversion of time column (DATE, DATETIME,TIMESTAMP) from integer -// to time if the field contain integer (int64). -// -// _txlock: The locking behavior to use when beginning a transaction. May be -// "deferred" (the default), "immediate", or "exclusive" (case insensitive). See: -// https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions -func (d *Driver) Open(name string) (conn driver.Conn, err error) { - if dmesgs { - defer func() { - dmesg("name %q: (driver.Conn %p, err %v)", name, conn, err) - }() - } - c, err := newConn(name) - if err != nil { - return nil, err - } - - for _, udf := range d.udfs { - if err = c.createFunctionInternal(udf); err != nil { - c.Close() - return nil, err - } - } - for _, coll := range d.collations { - if err = c.createCollationInternal(coll); err != nil { - c.Close() - return nil, err - } - } - for _, connHookFn := range d.connectionHooks { - if err = connHookFn(c, name); err != nil { - c.Close() - return nil, fmt.Errorf("connection hook: %w", err) - } - } - return c, nil -} - // FunctionContext represents the context user defined functions execute in. // Fields and/or methods of this type may get addedd in the future. type FunctionContext struct { @@ -2260,12 +501,6 @@ func registerFunction( return nil } -// RegisterConnectionHook registers a function to be called after each connection -// is opened. This is called after all the connection has been set up. -func (d *Driver) RegisterConnectionHook(fn ConnectionHookFn) { - d.connectionHooks = append(d.connectionHooks, fn) -} - // RegisterConnectionHook registers a function to be called after each connection // is opened. This is called after all the connection has been set up. func RegisterConnectionHook(fn ConnectionHookFn) { @@ -2512,6 +747,21 @@ func funcTrampoline(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { } } +// sqlite3AllocCString allocates a NUL-terminated copy of s using SQLite's +// memory allocator (sqlite3_malloc). The caller must arrange for SQLite to +// free the returned pointer via sqlite3_free. +func sqlite3AllocCString(tls *libc.TLS, s string) uintptr { + n := len(s) + 1 + p := sqlite3.Xsqlite3_malloc(tls, int32(n)) + if p == 0 { + return 0 + } + mem := (*libc.RawMem)(unsafe.Pointer(p))[:n:n] + copy(mem, []byte(s)) + mem[n-1] = 0 + return p +} + func stepTrampoline(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { impl, _ := makeAggregate(tls, ctx) if impl == nil { @@ -2604,13 +854,6 @@ func collationTrampoline(tls *libc.TLS, pApp uintptr, nLeft int32, zLeft uintptr } } -// C documentation -// -// int sqlite3_limit(sqlite3*, int id, int newVal); -func (c *conn) limit(id int, newVal int) int { - return int(sqlite3.Xsqlite3_limit(c.tls, c.db, int32(id), int32(newVal))) -} - // Limit calls sqlite3_limit, see the docs at // https://www.sqlite.org/c3ref/limit.html for details. // @@ -2631,29 +874,3 @@ func Limit(c *sql.Conn, id int, newVal int) (r int, err error) { return r, err } - -// C documentation -// -// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) { - if value == nil { - if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { - return 0, c.errstr(rc) - } - return 0, nil - } - - p, err := c.malloc(len(value)) - if err != nil { - return 0, err - } - if len(value) != 0 { - copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value) - } - if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { - c.free(p) - return 0, c.errstr(rc) - } - - return p, nil -} diff --git a/vendor/modernc.org/sqlite/sqlite_go18.go b/vendor/modernc.org/sqlite/sqlite_go18.go deleted file mode 100644 index 6e720e8..0000000 --- a/vendor/modernc.org/sqlite/sqlite_go18.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 The Sqlite Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.8 -// +build go1.8 - -package sqlite // import "modernc.org/sqlite" - -import ( - "context" - "database/sql/driver" -) - -// Ping implements driver.Pinger -func (c *conn) Ping(ctx context.Context) (err error) { - if dmesgs { - defer func() { - dmesg("conn %p, ctx %p: err %v", c, ctx, err) - }() - } - _, err = c.ExecContext(ctx, "select 1", nil) - return err -} - -// BeginTx implements driver.ConnBeginTx -func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (dt driver.Tx, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, ctx %p, opts %+v: (driver.Tx %v, err %v)", c, ctx, opts, dt, err) - }() - } - return c.begin(ctx, opts) -} - -// PrepareContext implements driver.ConnPrepareContext -func (c *conn) PrepareContext(ctx context.Context, query string) (ds driver.Stmt, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, ctx %p, query %q: (driver.Stmt %v, err %v)", c, ctx, query, ds, err) - }() - } - return c.prepare(ctx, query) -} - -// ExecContext implements driver.ExecerContext -func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Result, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, ctx %p, query %q, args %v: (driver.Result %p, err %v)", c, ctx, query, args, dr, err) - }() - } - return c.exec(ctx, query, args) -} - -// QueryContext implements driver.QueryerContext -func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Rows, err error) { - if dmesgs { - defer func() { - dmesg("conn %p, ctx %p, query %q, args %v: (driver.Rows %p, err %v)", c, ctx, query, args, dr, err) - }() - } - return c.query(ctx, query, args) -} - -// ExecContext implements driver.StmtExecContext -func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (dr driver.Result, err error) { - if dmesgs { - defer func() { - dmesg("stmt %p, ctx %p, args %v: (driver.Result %p, err %v)", s, ctx, args, dr, err) - }() - } - return s.exec(ctx, args) -} - -// QueryContext implements driver.StmtQueryContext -func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (dr driver.Rows, err error) { - if dmesgs { - defer func() { - dmesg("stmt %p, ctx %p, args %v: (driver.Rows %p, err %v)", s, ctx, args, dr, err) - }() - } - return s.query(ctx, args) -} diff --git a/vendor/modernc.org/sqlite/stmt.go b/vendor/modernc.org/sqlite/stmt.go new file mode 100644 index 0000000..f585205 --- /dev/null +++ b/vendor/modernc.org/sqlite/stmt.go @@ -0,0 +1,472 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "context" + "database/sql/driver" + "sync/atomic" + "unsafe" + + "modernc.org/libc" + sqlite3 "modernc.org/sqlite/lib" +) + +type stmt struct { + c *conn + psql uintptr + pstmt uintptr // The cached SQLite statement handle +} + +func newStmt(c *conn, sql string) (*stmt, error) { + p, err := libc.CString(sql) + if err != nil { + return nil, err + } + s := &stmt{c: c, psql: p} + + // Attempt to prepare the statement immediately + // We make a copy of the pointer because prepareV2 advances it + psql := p + pstmt, err := c.prepareV2(&psql) + if err != nil { + c.free(p) + return nil, err + } + + // Check if there is trailing SQL (indicating a script/multi-statement) + // If *psql (the tail) is 0, we consumed the whole string. + hasTail := *(*byte)(unsafe.Pointer(psql)) != 0 + if pstmt != 0 && !hasTail { + // Optimization: Single statement. Cache it. + s.pstmt = pstmt + return s, nil + } + + // It is either a script (hasTail) or a comment-only string (pstmt==0). + // For scripts: Finalize now. We will re-parse iteratively in Exec/Query + // to handle the multiple statements correctly using the existing loop logic. + if pstmt != 0 { + if err := c.finalize(pstmt); err != nil { + c.free(p) + return nil, err + } + } + + return s, nil +} + +// Close closes the statement. +// +// As of Go 1.1, a Stmt will not be closed if it's in use by any queries. +func (s *stmt) Close() (err error) { + if s.pstmt != 0 { + if e := s.c.finalize(s.pstmt); e != nil { + err = e + } + s.pstmt = 0 + } + if s.psql != 0 { + s.c.free(s.psql) + s.psql = 0 + } + return err +} + +// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE. +// +// Deprecated: Drivers should implement StmtExecContext instead (or +// additionally). +func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { //TODO StmtExecContext + return s.exec(context.Background(), toNamedValues(args)) +} + +// toNamedValues converts []driver.Value to []driver.NamedValue +func toNamedValues(vals []driver.Value) (r []driver.NamedValue) { + r = make([]driver.NamedValue, len(vals)) + for i, val := range vals { + r[i] = driver.NamedValue{Value: val, Ordinal: i + 1} + } + return r +} + +func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { + var pstmt uintptr + var done int32 + if ctx != nil { + if ctxDone := ctx.Done(); ctxDone != nil { + select { + case <-ctxDone: + return nil, ctx.Err() + default: + } + defer interruptOnDone(ctx, s.c, &done)() + } + } + + defer func() { + if ctx != nil && atomic.LoadInt32(&done) != 0 { + r, err = nil, ctx.Err() + } + if pstmt != 0 { + // ensure stmt finalized. + e := s.c.finalize(pstmt) + + if err == nil && e != nil { + // prioritize original + // returned error. + err = e + } + } + }() + + // OPTIMIZED PATH: Single Cached Statement + if s.pstmt != 0 { + err = func() error { + // Bind + n, err := s.c.bindParameterCount(s.pstmt) + if err != nil { + return err + } + if n != 0 { + allocs, err := s.c.bind(s.pstmt, n, args) + if err != nil { + return err + } + // Free allocations after step + if len(allocs) != 0 { + defer func() { + for _, v := range allocs { + s.c.free(v) + } + }() + } + } + + // Step + rc, err := s.c.step(s.pstmt) + if err != nil { + return err + } + + // Handle Result + switch rc & 0xff { + case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: + r, err = newResult(s.c) + default: + return s.c.errstr(int32(rc)) + } + return nil + }() + + // RESET (Crucial: Do not finalize) + // We must reset the VM to allow reuse. + // We also clear bindings to prevent leaking memory or state to next call. + if resetErr := s.c.reset(s.pstmt); resetErr != nil && err == nil { + err = resetErr + } + if clearErr := s.c.clearBindings(s.pstmt); clearErr != nil && err == nil { + err = clearErr + } + return r, err + } + + // FALLBACK PATH: Multi-statement script + for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { + if pstmt, err = s.c.prepareV2(&psql); err != nil { + return nil, err + } + + if pstmt == 0 { + continue + } + err = func() (err error) { + n, err := s.c.bindParameterCount(pstmt) + if err != nil { + return err + } + + if n != 0 { + allocs, err := s.c.bind(pstmt, n, args) + if err != nil { + return err + } + + if len(allocs) != 0 { + defer func() { + for _, v := range allocs { + s.c.free(v) + } + }() + } + } + + rc, err := s.c.step(pstmt) + if err != nil { + return err + } + + switch rc & 0xff { + case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: + r, err = newResult(s.c) + default: + return s.c.errstr(int32(rc)) + } + + return nil + }() + + e := s.c.finalize(pstmt) + pstmt = 0 // done with + + if err == nil && e != nil { + // prioritize original + // returned error. + err = e + } + + if err != nil { + return nil, err + } + } + return r, err +} + +// NumInput returns the number of placeholder parameters. +// +// If NumInput returns >= 0, the sql package will sanity check argument counts +// from callers and return errors to the caller before the statement's Exec or +// Query methods are called. +// +// NumInput may also return -1, if the driver doesn't know its number of +// placeholders. In that case, the sql package will not sanity check Exec or +// Query argument counts. +func (s *stmt) NumInput() (n int) { + return -1 +} + +// Query executes a query that may return rows, such as a +// SELECT. +// +// Deprecated: Drivers should implement StmtQueryContext instead (or +// additionally). +func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { //TODO StmtQueryContext + return s.query(context.Background(), toNamedValues(args)) +} + +func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { + var pstmt uintptr + var done int32 + if ctx != nil { + if ctxDone := ctx.Done(); ctxDone != nil { + select { + case <-ctxDone: + return nil, ctx.Err() + default: + } + defer interruptOnDone(ctx, s.c, &done)() + } + } + + var allocs []uintptr + + defer func() { + if ctx != nil && atomic.LoadInt32(&done) != 0 { + if r != nil { + r.Close() + } + r, err = nil, ctx.Err() + } else if r == nil && err == nil { + r, err = newRows(s.c, pstmt, allocs, true) + } + + if pstmt != 0 { + // ensure stmt finalized. + e := s.c.finalize(pstmt) + + if err == nil && e != nil { + // prioritize original + // returned error. + err = e + } + } + + }() + + // OPTIMIZED PATH: Single Cached Statement + if s.pstmt != 0 { + // Bind + n, err := s.c.bindParameterCount(s.pstmt) + if err != nil { + return nil, err + } + if n != 0 { + if allocs, err = s.c.bind(s.pstmt, n, args); err != nil { + return nil, err + } + } + + // Step + rc, err := s.c.step(s.pstmt) + if err != nil { + // On error, we must free allocs manually because 'newRows' won't take ownership + for _, v := range allocs { + s.c.free(v) + } + s.c.reset(s.pstmt) + s.c.clearBindings(s.pstmt) + return nil, err + } + + // Handle Result + switch rc & 0xff { + case sqlite3.SQLITE_ROW: + // Pass reuseStmt=true + if r, err = newRows(s.c, s.pstmt, allocs, false); err != nil { + s.c.reset(s.pstmt) + s.c.clearBindings(s.pstmt) + return nil, err + } + r.(*rows).reuseStmt = true + return r, nil + + case sqlite3.SQLITE_DONE: + // No rows. Reset immediately. + // We still return a rows object (empty), but we can reset the stmt now + // because the empty rows object won't call step() again. + // However, standard newRows behavior expects a valid stmt to get columns. + // Let's rely on newRows to read columns, then it returns. + + // Actually, if we pass reuseStmt=true to an empty set, + // rows.Close() will eventually reset it. + if r, err = newRows(s.c, s.pstmt, allocs, true); err != nil { + s.c.reset(s.pstmt) + s.c.clearBindings(s.pstmt) + return nil, err + } + r.(*rows).reuseStmt = true + return r, nil + + default: + // Error case + for _, v := range allocs { + s.c.free(v) + } + s.c.reset(s.pstmt) + s.c.clearBindings(s.pstmt) + return nil, s.c.errstr(int32(rc)) + } + } + + // FALLBACK PATH: Multi-statement script + for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { + if pstmt, err = s.c.prepareV2(&psql); err != nil { + return nil, err + } + + if pstmt == 0 { + continue + } + + err = func() (err error) { + n, err := s.c.bindParameterCount(pstmt) + if err != nil { + return err + } + + if n != 0 { + if allocs, err = s.c.bind(pstmt, n, args); err != nil { + return err + } + } + + rc, err := s.c.step(pstmt) + if err != nil { + return err + } + + switch rc & 0xff { + case sqlite3.SQLITE_ROW: + if r != nil { + r.Close() + } + if r, err = newRows(s.c, pstmt, allocs, false); err != nil { + return err + } + + pstmt = 0 + return nil + case sqlite3.SQLITE_DONE: + if r == nil { + if r, err = newRows(s.c, pstmt, allocs, true); err != nil { + return err + } + pstmt = 0 + return nil + } + + // nop + default: + return s.c.errstr(int32(rc)) + } + + if *(*byte)(unsafe.Pointer(psql)) == 0 { + if r != nil { + r.Close() + } + if r, err = newRows(s.c, pstmt, allocs, true); err != nil { + return err + } + + pstmt = 0 + } + return nil + }() + + e := s.c.finalize(pstmt) + pstmt = 0 // done with + + if err == nil && e != nil { + // prioritize original + // returned error. + err = e + } + + if err != nil { + return nil, err + } + } + return r, err +} + +// ExecContext implements driver.StmtExecContext +func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (dr driver.Result, err error) { + if dmesgs { + defer func() { + dmesg("stmt %p, ctx %p, args %v: (driver.Result %p, err %v)", s, ctx, args, dr, err) + }() + } + return s.exec(ctx, args) +} + +// QueryContext implements driver.StmtQueryContext +func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (dr driver.Rows, err error) { + if dmesgs { + defer func() { + dmesg("stmt %p, ctx %p, args %v: (driver.Rows %p, err %v)", s, ctx, args, dr, err) + }() + } + return s.query(ctx, args) +} + +// C documentation +// +// int sqlite3_clear_bindings(sqlite3_stmt*); +func (c *conn) clearBindings(pstmt uintptr) error { + if rc := sqlite3.Xsqlite3_clear_bindings(c.tls, pstmt); rc != sqlite3.SQLITE_OK { + return c.errstr(rc) + } + return nil +} diff --git a/vendor/modernc.org/sqlite/tx.go b/vendor/modernc.org/sqlite/tx.go new file mode 100644 index 0000000..710664f --- /dev/null +++ b/vendor/modernc.org/sqlite/tx.go @@ -0,0 +1,62 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "context" + "database/sql/driver" + + "modernc.org/libc" + sqlite3 "modernc.org/sqlite/lib" +) + +type tx struct { + c *conn +} + +func newTx(ctx context.Context, c *conn, opts driver.TxOptions) (*tx, error) { + r := &tx{c: c} + + sql := "begin" + if !opts.ReadOnly && c.beginMode != "" { + sql = "begin " + c.beginMode + } + + if err := r.exec(ctx, sql); err != nil { + return nil, err + } + + return r, nil +} + +// Commit implements driver.Tx. +func (t *tx) Commit() (err error) { + return t.exec(context.Background(), "commit") +} + +// Rollback implements driver.Tx. +func (t *tx) Rollback() (err error) { + return t.exec(context.Background(), "rollback") +} + +func (t *tx) exec(ctx context.Context, sql string) (err error) { + psql, err := libc.CString(sql) + if err != nil { + return err + } + + defer t.c.free(psql) + //TODO use t.conn.ExecContext() instead + + if ctx != nil && ctx.Done() != nil { + defer interruptOnDone(ctx, t.c, nil)() + } + + if rc := sqlite3.Xsqlite3_exec(t.c.tls, t.c.db, psql, 0, 0, 0); rc != sqlite3.SQLITE_OK { + return t.c.errstr(rc) + } + + return nil +} diff --git a/vendor/modernc.org/sqlite/vtab.go b/vendor/modernc.org/sqlite/vtab.go new file mode 100644 index 0000000..6fe1fed --- /dev/null +++ b/vendor/modernc.org/sqlite/vtab.go @@ -0,0 +1,872 @@ +// Copyright 2025 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sqlite // import "modernc.org/sqlite" + +import ( + "fmt" + "math" + "sync" + "unsafe" + + "modernc.org/libc" + sqlite3 "modernc.org/sqlite/lib" + "modernc.org/sqlite/vtab" +) + +func init() { + vtab.SetRegisterFunc(registerModule) +} + +var ( + // vtabModules tracks Go virtual table modules registered via the vtab + // package. Each module is identified by an integer ID used as pAux when + // calling sqlite3_create_module_v2, so that trampolines can recover the + // Go Module implementation. + vtabModules = struct { + mu sync.RWMutex + m map[uintptr]*goModule + ids idGen + // name2id keeps stable IDs per module name to avoid unbounded growth + // across connections. + name2id map[string]uintptr + }{ + m: make(map[uintptr]*goModule), + name2id: make(map[string]uintptr), + } + + // nativeModules holds sqlite3_module instances for registered modules. We + // keep them in Go memory so their addresses remain stable for the C layer. + nativeModules = struct { + mu sync.RWMutex + m map[string]*sqlite3.Sqlite3_module + }{ + m: make(map[string]*sqlite3.Sqlite3_module), + } + + // vtabTables maps sqlite3_vtab* (pVtab) to the corresponding Go Table. + vtabTables = struct { + mu sync.RWMutex + m map[uintptr]*goTable + }{ + m: make(map[uintptr]*goTable), + } + + // vtabCursors maps sqlite3_vtab_cursor* (pCursor) to the corresponding Go + // Cursor. + vtabCursors = struct { + mu sync.RWMutex + m map[uintptr]*goCursor + }{ + m: make(map[uintptr]*goCursor), + } +) + +// goModule wraps a vtab.Module implementation with its name. +type goModule struct { + name string + impl vtab.Module +} + +// goTable wraps a vtab.Table implementation and remembers its module. +type goTable struct { + mod *goModule + impl vtab.Table +} + +// goCursor wraps a vtab.Cursor implementation and remembers its table. +type goCursor struct { + table *goTable + impl vtab.Cursor +} + +// Use aliases of the underlying lib types so field layouts remain correct. +type cIndexConstraint = sqlite3.Tsqlite3_index_constraint +type cIndexOrderBy = sqlite3.Tsqlite3_index_orderby +type cConstraintUsage = sqlite3.Tsqlite3_index_constraint_usage + +// registerModule is installed as the hook for vtab.RegisterModule. +func registerModule(name string, m vtab.Module) error { + if _, exists := d.modules[name]; exists { + return fmt.Errorf("sqlite: module %q already registered", name) + } + d.modules[name] = m + return nil +} + +// registerModules installs all globally registered vtab modules on this +// connection by calling sqlite3_create_module_v2 for each one. +func (c *conn) registerModules() error { + for name, mod := range d.modules { + if err := c.registerSingleModule(name, mod); err != nil { + return err + } + } + return nil +} + +func (c *conn) registerSingleModule(name string, m vtab.Module) error { + // Allocate or reuse a stable ID for this module name and remember the Go implementation. + vtabModules.mu.Lock() + modID, ok := vtabModules.name2id[name] + if !ok { + modID = vtabModules.ids.next() + vtabModules.name2id[name] = modID + } + vtabModules.m[modID] = &goModule{name: name, impl: m} + vtabModules.mu.Unlock() + + nativeModules.mu.Lock() + defer nativeModules.mu.Unlock() + if _, exists := nativeModules.m[name]; exists { + // Module struct already created; nothing more to do for this connection. + return nil + } + + // Build a sqlite3_module descriptor with trampolines. + mod := &sqlite3.Sqlite3_module{} + mod.FiVersion = 1 + mod.FxCreate = cFuncPointer(vtabCreateTrampoline) + mod.FxConnect = cFuncPointer(vtabConnectTrampoline) + mod.FxBestIndex = cFuncPointer(vtabBestIndexTrampoline) + mod.FxDisconnect = cFuncPointer(vtabDisconnectTrampoline) + mod.FxDestroy = cFuncPointer(vtabDestroyTrampoline) + mod.FxOpen = cFuncPointer(vtabOpenTrampoline) + mod.FxClose = cFuncPointer(vtabCloseTrampoline) + mod.FxFilter = cFuncPointer(vtabFilterTrampoline) + mod.FxNext = cFuncPointer(vtabNextTrampoline) + mod.FxEof = cFuncPointer(vtabEofTrampoline) + mod.FxColumn = cFuncPointer(vtabColumnTrampoline) + mod.FxRowid = cFuncPointer(vtabRowidTrampoline) + mod.FxFindFunction = cFuncPointer(vtabFindFunctionTrampoline) + mod.FxRename = cFuncPointer(vtabRenameTrampoline) + mod.FxUpdate = cFuncPointer(vtabUpdateTrampoline) + mod.FxBegin = cFuncPointer(vtabBeginTrampoline) + mod.FxSync = cFuncPointer(vtabSyncTrampoline) + mod.FxCommit = cFuncPointer(vtabCommitTrampoline) + mod.FxRollback = cFuncPointer(vtabRollbackTrampoline) + mod.FxSavepoint = cFuncPointer(vtabSavepointTrampoline) + mod.FxRelease = cFuncPointer(vtabReleaseTrampoline) + mod.FxRollbackTo = cFuncPointer(vtabRollbackToTrampoline) + + nativeModules.m[name] = mod + + // Prepare C string for module name. + zName, err := libc.CString(name) + if err != nil { + return err + } + defer libc.Xfree(c.tls, zName) + + // Register the module with this connection. + if rc := sqlite3.Xsqlite3_create_module_v2(c.tls, c.db, zName, uintptr(unsafe.Pointer(mod)), modID, 0); rc != sqlite3.SQLITE_OK { + return fmt.Errorf("create_module %q: %w", name, c.errstr(rc)) + } + return nil +} + +// vtabCreateTrampoline is the xCreate callback. It invokes the corresponding +// Go vtab.Module.Create method, declares a default schema based on argv, and +// allocates a sqlite3_vtab. +func vtabCreateTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32, argv uintptr, ppVtab uintptr, pzErr uintptr) int32 { + gm := lookupGoModule(pAux) + if gm == nil { + setVtabError(tls, pzErr, fmt.Sprintf("vtab: unknown module id %d", pAux)) + return sqlite3.SQLITE_ERROR + } + args := extractVtabArgs(tls, argc, argv) + ctx := vtab.NewContext(func(schema string) error { + zSchema, err := libc.CString(schema) + if err != nil { + return err + } + defer libc.Xfree(tls, zSchema) + if rc := sqlite3.Xsqlite3_declare_vtab(tls, db, zSchema); rc != sqlite3.SQLITE_OK { + return fmt.Errorf("declare_vtab failed: rc=%d", rc) + } + return nil + }) + tbl, err := gm.impl.Create(ctx, args) + if err != nil { + setVtabError(tls, pzErr, err.Error()) + return sqlite3.SQLITE_ERROR + } + sz := unsafe.Sizeof(sqlite3.Sqlite3_vtab{}) + p := sqlite3.Xsqlite3_malloc(tls, int32(sz)) + if p == 0 { + setVtabError(tls, pzErr, "vtab: out of memory") + return sqlite3.SQLITE_NOMEM + } + mem := (*libc.RawMem)(unsafe.Pointer(p))[:sz:sz] + for i := range mem { + mem[i] = 0 + } + *(*uintptr)(unsafe.Pointer(ppVtab)) = p + + gt := &goTable{mod: gm, impl: tbl} + vtabTables.mu.Lock() + vtabTables.m[p] = gt + vtabTables.mu.Unlock() + return sqlite3.SQLITE_OK +} + +// vtabConnectTrampoline is the xConnect callback. It mirrors +// vtabCreateTrampoline but calls Module.Connect. +func vtabConnectTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32, argv uintptr, ppVtab uintptr, pzErr uintptr) int32 { + gm := lookupGoModule(pAux) + if gm == nil { + setVtabError(tls, pzErr, fmt.Sprintf("vtab: unknown module id %d", pAux)) + return sqlite3.SQLITE_ERROR + } + args := extractVtabArgs(tls, argc, argv) + ctx := vtab.NewContext(func(schema string) error { + zSchema, err := libc.CString(schema) + if err != nil { + return err + } + defer libc.Xfree(tls, zSchema) + if rc := sqlite3.Xsqlite3_declare_vtab(tls, db, zSchema); rc != sqlite3.SQLITE_OK { + return fmt.Errorf("declare_vtab failed: rc=%d", rc) + } + return nil + }) + tbl, err := gm.impl.Connect(ctx, args) + if err != nil { + setVtabError(tls, pzErr, err.Error()) + return sqlite3.SQLITE_ERROR + } + sz := unsafe.Sizeof(sqlite3.Sqlite3_vtab{}) + p := sqlite3.Xsqlite3_malloc(tls, int32(sz)) + if p == 0 { + setVtabError(tls, pzErr, "vtab: out of memory") + return sqlite3.SQLITE_NOMEM + } + mem := (*libc.RawMem)(unsafe.Pointer(p))[:sz:sz] + for i := range mem { + mem[i] = 0 + } + *(*uintptr)(unsafe.Pointer(ppVtab)) = p + + gt := &goTable{mod: gm, impl: tbl} + vtabTables.mu.Lock() + vtabTables.m[p] = gt + vtabTables.mu.Unlock() + return sqlite3.SQLITE_OK +} + +// vtabBestIndexTrampoline maps sqlite3_index_info to vtab.IndexInfo and +// delegates to Table.BestIndex. It also mirrors constraint and ORDER BY +// information into the Go structure. +func vtabBestIndexTrampoline(tls *libc.TLS, pVtab uintptr, pInfo uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + + idx := (*sqlite3.Sqlite3_index_info)(unsafe.Pointer(pInfo)) + info := &vtab.IndexInfo{} + + // Populate Constraints from sqlite3_index_info.aConstraint. + if idx.FnConstraint > 0 && idx.FaConstraint != 0 { + n := int(idx.FnConstraint) + cs := make([]vtab.Constraint, 0, n) + base := idx.FaConstraint + sz := unsafe.Sizeof(cIndexConstraint{}) + for i := 0; i < n; i++ { + c := (*cIndexConstraint)(unsafe.Pointer(base + uintptr(i)*sz)) + op := vtab.OpUnknown + switch int32(c.Fop) { + case sqlite3.SQLITE_INDEX_CONSTRAINT_EQ: + op = vtab.OpEQ + case sqlite3.SQLITE_INDEX_CONSTRAINT_GT: + op = vtab.OpGT + case sqlite3.SQLITE_INDEX_CONSTRAINT_LE: + op = vtab.OpLE + case sqlite3.SQLITE_INDEX_CONSTRAINT_LT: + op = vtab.OpLT + case sqlite3.SQLITE_INDEX_CONSTRAINT_GE: + op = vtab.OpGE + case sqlite3.SQLITE_INDEX_CONSTRAINT_MATCH: + op = vtab.OpMATCH + case sqlite3.SQLITE_INDEX_CONSTRAINT_NE: + op = vtab.OpNE + case sqlite3.SQLITE_INDEX_CONSTRAINT_IS: + op = vtab.OpIS + case sqlite3.SQLITE_INDEX_CONSTRAINT_ISNOT: + op = vtab.OpISNOT + case sqlite3.SQLITE_INDEX_CONSTRAINT_ISNULL: + op = vtab.OpISNULL + case sqlite3.SQLITE_INDEX_CONSTRAINT_ISNOTNULL: + op = vtab.OpISNOTNULL + case sqlite3.SQLITE_INDEX_CONSTRAINT_LIKE: + op = vtab.OpLIKE + case sqlite3.SQLITE_INDEX_CONSTRAINT_GLOB: + op = vtab.OpGLOB + case sqlite3.SQLITE_INDEX_CONSTRAINT_REGEXP: + op = vtab.OpREGEXP + case sqlite3.SQLITE_INDEX_CONSTRAINT_FUNCTION: + op = vtab.OpFUNCTION + case sqlite3.SQLITE_INDEX_CONSTRAINT_LIMIT: + op = vtab.OpLIMIT + case sqlite3.SQLITE_INDEX_CONSTRAINT_OFFSET: + op = vtab.OpOFFSET + } + cs = append(cs, vtab.Constraint{ + Column: int(c.FiColumn), + Op: op, + Usable: c.Fusable != 0, + ArgIndex: -1, // 0-based; -1 means ignore + Omit: false, + }) + } + info.Constraints = cs + } + + // Populate OrderBy from sqlite3_index_info.aOrderBy. + if idx.FnOrderBy > 0 && idx.FaOrderBy != 0 { + n := int(idx.FnOrderBy) + obs := make([]vtab.OrderBy, 0, n) + base := idx.FaOrderBy + sz := unsafe.Sizeof(cIndexOrderBy{}) + for i := 0; i < n; i++ { + ob := (*cIndexOrderBy)(unsafe.Pointer(base + uintptr(i)*sz)) + obs = append(obs, vtab.OrderBy{ + Column: int(ob.FiColumn), + Desc: ob.Fdesc != 0, + }) + } + info.OrderBy = obs + } + + // Populate ColUsed and idxFlags for module visibility. + if idx.FcolUsed != 0 { + info.ColUsed = uint64(idx.FcolUsed) + } + if idx.FidxFlags != 0 { + info.IdxFlags = int(idx.FidxFlags) + } + + if err := gt.impl.BestIndex(info); err != nil { + // Report error via zErrMsg on pVtab. + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + + // Propagate any ArgIndex assignments back into aConstraintUsage so that + // SQLite will populate xFilter's argv[] accordingly. + if idx.FnConstraint > 0 && idx.FaConstraintUsage != 0 && len(info.Constraints) > 0 { + n := int(idx.FnConstraint) + base := idx.FaConstraintUsage + sz := unsafe.Sizeof(cConstraintUsage{}) + for i := 0; i < n && i < len(info.Constraints); i++ { + cu := (*cConstraintUsage)(unsafe.Pointer(base + uintptr(i)*sz)) + argIndex := info.Constraints[i].ArgIndex + if argIndex >= 0 { + // Go ArgIndex is 0-based; SQLite wants 1-based. + cu.FargvIndex = int32(argIndex + 1) + } + if info.Constraints[i].Omit { + cu.Fomit = 1 + } + } + } + // Guard against int32 overflow: SQLite expects idxNum as int32. + if info.IdxNum < math.MinInt32 || info.IdxNum > math.MaxInt32 { + setVtabZErrMsg(tls, pVtab, fmt.Sprintf("vtab: IdxNum %d out of int32 range", info.IdxNum)) + return sqlite3.SQLITE_ERROR + } + idx.FidxNum = int32(info.IdxNum) + if info.IdxStr != "" { + // Allocate using SQLite allocator because needToFreeIdxStr=1 instructs + // SQLite to free the string with sqlite3_free. + z := sqlite3AllocCString(tls, info.IdxStr) + if z != 0 { + idx.FidxStr = z + idx.FneedToFreeIdxStr = 1 + } + } + if info.OrderByConsumed { + idx.ForderByConsumed = 1 + } + if info.IdxFlags != 0 { + idx.FidxFlags = int32(info.IdxFlags) + } + if info.EstimatedCost != 0 { + idx.FestimatedCost = info.EstimatedCost + } + if info.EstimatedRows != 0 { + idx.FestimatedRows = sqlite3.Sqlite3_int64(info.EstimatedRows) + } + return sqlite3.SQLITE_OK +} + +// vtabDisconnectTrampoline is xDisconnect. It frees the sqlite3_vtab and +// calls Table.Disconnect. +func vtabDisconnectTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt != nil { + _ = gt.impl.Disconnect() + vtabTables.mu.Lock() + delete(vtabTables.m, pVtab) + vtabTables.mu.Unlock() + } + sqlite3.Xsqlite3_free(tls, pVtab) + return sqlite3.SQLITE_OK +} + +// vtabDestroyTrampoline is xDestroy. Currently identical to Disconnect. +func vtabDestroyTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt != nil { + _ = gt.impl.Destroy() + vtabTables.mu.Lock() + delete(vtabTables.m, pVtab) + vtabTables.mu.Unlock() + } + sqlite3.Xsqlite3_free(tls, pVtab) + return sqlite3.SQLITE_OK +} + +// vtabOpenTrampoline is xOpen. It allocates an empty sqlite3_vtab_cursor and +// creates a Go Cursor via Table.Open. +func vtabOpenTrampoline(tls *libc.TLS, pVtab uintptr, ppCursor uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + + curImpl, err := gt.impl.Open() + if err != nil { + return sqlite3.SQLITE_ERROR + } + + sz := unsafe.Sizeof(sqlite3.Sqlite3_vtab_cursor{}) + p := sqlite3.Xsqlite3_malloc(tls, int32(sz)) + if p == 0 { + return sqlite3.SQLITE_NOMEM + } + mem := (*libc.RawMem)(unsafe.Pointer(p))[:sz:sz] + for i := range mem { + mem[i] = 0 + } + *(*uintptr)(unsafe.Pointer(ppCursor)) = p + + // Link cursor back to its vtab so error reporting can set zErrMsg. + cur := (*sqlite3.Sqlite3_vtab_cursor)(unsafe.Pointer(p)) + cur.FpVtab = pVtab + + gc := &goCursor{table: gt, impl: curImpl} + vtabCursors.mu.Lock() + vtabCursors.m[p] = gc + vtabCursors.mu.Unlock() + return sqlite3.SQLITE_OK +} + +// vtabCloseTrampoline is xClose. It frees the sqlite3_vtab_cursor and calls +// Cursor.Close. +func vtabCloseTrampoline(tls *libc.TLS, pCursor uintptr) int32 { + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc != nil { + _ = gc.impl.Close() + vtabCursors.mu.Lock() + delete(vtabCursors.m, pCursor) + vtabCursors.mu.Unlock() + } + sqlite3.Xsqlite3_free(tls, pCursor) + return sqlite3.SQLITE_OK +} + +// vtabFilterTrampoline is xFilter. +func vtabFilterTrampoline(tls *libc.TLS, pCursor uintptr, idxNum int32, idxStr uintptr, argc int32, argv uintptr) int32 { + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc == nil { + return sqlite3.SQLITE_ERROR + } + + var idxStrGo string + if idxStr != 0 { + idxStrGo = libc.GoString(idxStr) + } + vals := functionArgs(tls, argc, argv) + if err := gc.impl.Filter(int(idxNum), idxStrGo, vals); err != nil { + // Set zErrMsg on the associated vtab for better diagnostics. + if pCursor != 0 { + cur := (*sqlite3.Sqlite3_vtab_cursor)(unsafe.Pointer(pCursor)) + if cur.FpVtab != 0 { + setVtabZErrMsg(tls, cur.FpVtab, err.Error()) + } + } + return sqlite3.SQLITE_ERROR + } + return sqlite3.SQLITE_OK +} + +// vtabNextTrampoline is xNext. +func vtabNextTrampoline(tls *libc.TLS, pCursor uintptr) int32 { + _ = tls + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc == nil { + return sqlite3.SQLITE_ERROR + } + if err := gc.impl.Next(); err != nil { + return sqlite3.SQLITE_ERROR + } + return sqlite3.SQLITE_OK +} + +// vtabEofTrampoline is xEof. +func vtabEofTrampoline(tls *libc.TLS, pCursor uintptr) int32 { + _ = tls + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc == nil || gc.impl.Eof() { + return 1 + } + return 0 +} + +// vtabColumnTrampoline is xColumn. +func vtabColumnTrampoline(tls *libc.TLS, pCursor uintptr, ctx uintptr, iCol int32) int32 { + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc == nil { + sqlite3.Xsqlite3_result_null(tls, ctx) + return sqlite3.SQLITE_OK + } + val, err := gc.impl.Column(int(iCol)) + if err != nil { + // Report via result error on the context. + z, cerr := libc.CString(err.Error()) + if cerr == nil { + defer libc.Xfree(tls, z) + sqlite3.Xsqlite3_result_error(tls, ctx, z, -1) + sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) + } else { + sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) + } + return sqlite3.SQLITE_ERROR + } + if err := functionReturnValue(tls, ctx, val); err != nil { + // Include a descriptive error message for easier debugging + // (e.g., unsupported type conversions). + if err != nil { + z, cerr := libc.CString(err.Error()) + if cerr == nil { + defer libc.Xfree(tls, z) + sqlite3.Xsqlite3_result_error(tls, ctx, z, -1) + } + } + sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) + return sqlite3.SQLITE_ERROR + } + return sqlite3.SQLITE_OK +} + +// vtabRowidTrampoline is xRowid. +func vtabRowidTrampoline(tls *libc.TLS, pCursor uintptr, pRowid uintptr) int32 { + _ = tls + vtabCursors.mu.RLock() + gc := vtabCursors.m[pCursor] + vtabCursors.mu.RUnlock() + if gc == nil { + *(*int64)(unsafe.Pointer(pRowid)) = 0 + return sqlite3.SQLITE_OK + } + rowid, err := gc.impl.Rowid() + if err != nil { + *(*int64)(unsafe.Pointer(pRowid)) = 0 + return sqlite3.SQLITE_ERROR + } + *(*int64)(unsafe.Pointer(pRowid)) = rowid + return sqlite3.SQLITE_OK +} + +func lookupGoModule(id uintptr) *goModule { + vtabModules.mu.RLock() + defer vtabModules.mu.RUnlock() + return vtabModules.m[id] +} + +func extractVtabArgs(tls *libc.TLS, argc int32, argv uintptr) []string { + args := make([]string, argc) + for i := int32(0); i < argc; i++ { + cstr := *(*uintptr)(unsafe.Pointer(argv + uintptr(i)*unsafe.Sizeof(uintptr(0)))) + args[i] = libc.GoString(cstr) + } + return args +} + +func setVtabError(tls *libc.TLS, pzErr uintptr, msg string) { + if pzErr == 0 { + return + } + z := sqlite3AllocCString(tls, msg) + if z == 0 { + return + } + *(*uintptr)(unsafe.Pointer(pzErr)) = z +} + +// setVtabZErrMsg sets pVtab->zErrMsg to a newly allocated C string containing +// msg. SQLite will free this pointer. +func setVtabZErrMsg(tls *libc.TLS, pVtab uintptr, msg string) { + if pVtab == 0 { + return + } + vt := (*sqlite3.Sqlite3_vtab)(unsafe.Pointer(pVtab)) + if vt.FzErrMsg != 0 { + sqlite3.Xsqlite3_free(tls, vt.FzErrMsg) + vt.FzErrMsg = 0 + } + z := sqlite3AllocCString(tls, msg) + if z == 0 { + return + } + vt.FzErrMsg = z +} + +// Optional vtab callbacks + +// vtabFindFunctionTrampoline is xFindFunction. We currently do not expose a +// Go surface for per-table SQL functions; report not found (return 0). +func vtabFindFunctionTrampoline(tls *libc.TLS, pVtab uintptr, nArg int32, zName uintptr, pxFunc uintptr, ppArg uintptr) int32 { + _ = tls + _ = pVtab + _ = nArg + _ = zName + if pxFunc != 0 { + *(*uintptr)(unsafe.Pointer(pxFunc)) = 0 + } + if ppArg != 0 { + *(*uintptr)(unsafe.Pointer(ppArg)) = 0 + } + return 0 // not found +} + +// vtabRenameTrampoline is xRename. Calls Table.Rename if implemented. +func vtabRenameTrampoline(tls *libc.TLS, pVtab uintptr, zNew uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + name := libc.GoString(zNew) + if r, ok := gt.impl.(interface{ Rename(string) error }); ok { + if err := r.Rename(name); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +// vtabUpdateTrampoline is xUpdate. Not supported by default; report read-only. +func vtabUpdateTrampoline(tls *libc.TLS, pVtab uintptr, argc int32, argv uintptr, pRowid uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + upd, ok := gt.impl.(interface { + Insert(cols []vtab.Value, rowid *int64) error + Update(oldRowid int64, cols []vtab.Value, newRowid *int64) error + Delete(oldRowid int64) error + }) + if !ok { + return sqlite3.SQLITE_READONLY + } + + // DELETE: argc == 1; argv[0]=oldRowid + if argc == 1 { + valPtr := *(*uintptr)(unsafe.Pointer(argv)) + oldRowid := int64(0) + if sqlite3.Xsqlite3_value_type(tls, valPtr) != sqlite3.SQLITE_NULL { + oldRowid = int64(sqlite3.Xsqlite3_value_int64(tls, valPtr)) + } + if err := upd.Delete(oldRowid); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + return sqlite3.SQLITE_OK + } + + // INSERT or UPDATE: argc == N+2. argv[0]=oldRowid (NULL for insert), + // argv[1..N]=column values, argv[N+1]=newRowid (or desired rowid for insert, may be NULL). + if argc < 3 { + return sqlite3.SQLITE_MISUSE + } + nCols := argc - 2 + // Extract column values + colsPtr := argv + uintptr(1)*sqliteValPtrSize + cols := functionArgs(tls, nCols, colsPtr) + + // Determine old/new rowid + oldPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(0)*sqliteValPtrSize)) + newPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(argc-1)*sqliteValPtrSize)) + + oldIsNull := sqlite3.Xsqlite3_value_type(tls, oldPtr) == sqlite3.SQLITE_NULL + newIsNull := sqlite3.Xsqlite3_value_type(tls, newPtr) == sqlite3.SQLITE_NULL + + if oldIsNull { + // INSERT + var rid int64 + if !newIsNull { + rid = int64(sqlite3.Xsqlite3_value_int64(tls, newPtr)) + } + if err := upd.Insert(cols, &rid); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + if pRowid != 0 { + *(*int64)(unsafe.Pointer(pRowid)) = rid + } + return sqlite3.SQLITE_OK + } + + // UPDATE + oldRowid := int64(sqlite3.Xsqlite3_value_int64(tls, oldPtr)) + var newRid int64 + if !newIsNull { + newRid = int64(sqlite3.Xsqlite3_value_int64(tls, newPtr)) + } + if err := upd.Update(oldRowid, cols, &newRid); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + if pRowid != 0 && newRid != 0 { + *(*int64)(unsafe.Pointer(pRowid)) = newRid + } + return sqlite3.SQLITE_OK +} + +// Transactional callbacks +func vtabBeginTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Begin() error }); ok { + if err := tr.Begin(); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabSyncTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Sync() error }); ok { + if err := tr.Sync(); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabCommitTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Commit() error }); ok { + if err := tr.Commit(); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabRollbackTrampoline(tls *libc.TLS, pVtab uintptr) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Rollback() error }); ok { + if err := tr.Rollback(); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabSavepointTrampoline(tls *libc.TLS, pVtab uintptr, i int32) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Savepoint(int) error }); ok { + if err := tr.Savepoint(int(i)); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabReleaseTrampoline(tls *libc.TLS, pVtab uintptr, i int32) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ Release(int) error }); ok { + if err := tr.Release(int(i)); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} + +func vtabRollbackToTrampoline(tls *libc.TLS, pVtab uintptr, i int32) int32 { + vtabTables.mu.RLock() + gt := vtabTables.m[pVtab] + vtabTables.mu.RUnlock() + if gt == nil { + return sqlite3.SQLITE_ERROR + } + if tr, ok := gt.impl.(interface{ RollbackTo(int) error }); ok { + if err := tr.RollbackTo(int(i)); err != nil { + setVtabZErrMsg(tls, pVtab, err.Error()) + return sqlite3.SQLITE_ERROR + } + } + return sqlite3.SQLITE_OK +} diff --git a/vendor/modernc.org/sqlite/vtab/doc.go b/vendor/modernc.org/sqlite/vtab/doc.go new file mode 100644 index 0000000..e0cdf0a --- /dev/null +++ b/vendor/modernc.org/sqlite/vtab/doc.go @@ -0,0 +1,53 @@ +// Package vtab defines a Go-facing API for implementing SQLite virtual table +// modules on top of the modernc.org/sqlite driver. +// +// It is intentionally small and generic so that external projects can +// implement virtual tables without depending on the translated C internals. +package vtab + +// API notes +// +// - Schema declaration: Modules must call Context.Declare from within Create +// or Connect to declare the virtual table schema (a CREATE TABLE statement). +// The driver no longer auto-declares based on USING(...) args to support +// dynamic schemas (e.g., CSV headers). +// +// - Constraint operators: ConstraintOp includes OpUnknown for operators that +// are not recognized. The driver maps common SQLite operators including EQ, +// NE, GT, GE, LT, LE, MATCH, IS/ISNOT, ISNULL/ISNOTNULL, LIKE, GLOB, REGEXP, +// FUNCTION, LIMIT, and OFFSET. +// +// - ArgIndex: Set as 0-based in Go to indicate which position in argv[] should +// receive a constraint value. The driver adds +1 when communicating with +// SQLite (which is 1-based). Use -1 (default) to ignore. +// +// - Omit: Set Constraint.Omit to ask SQLite to not re-evaluate that constraint +// in the parent query if the virtual table fully handles it. +// +// - ColUsed: IndexInfo.ColUsed provides a bitmask of columns referenced by the +// query. Bit N indicates column N is used. +// +// - IdxFlags: IndexInfo.IdxFlags allows modules to set planning flags. +// Currently, IndexScanUnique (mirrors SQLITE_INDEX_SCAN_UNIQUE) indicates +// the plan will visit at most one row. This can help the optimizer. +// +// Optional interfaces +// +// - Updater: Implement on your Table to support writes via xUpdate. +// Insert(cols, *rowid), Update(oldRowid, cols, *newRowid), Delete(oldRowid). +// The trampoline maps SQLite xUpdate’s calling convention to these methods. +// +// - Renamer: Implement Rename(newName string) on your Table to handle xRename. +// If unimplemented, rename is treated as a no-op. +// +// - Transactional: Implement Begin/Sync/Commit/Rollback/Savepoint/Release/ +// RollbackTo as needed. Unimplemented methods are treated as no-ops. +// These callbacks let writable or advanced modules coordinate with SQLite +// transaction boundaries. +// +// Re-entrancy cautions +// - Avoid executing SQL on the same connection from within vtab methods +// (Create/Connect/BestIndex/Filter/etc.). SQLite virtual table callbacks run +// inside the engine and issuing re-entrant SQL on the same connection can +// lead to deadlocks or undefined behavior. If a module requires issuing SQL, +// consider using a separate connection and document the concurrency model. diff --git a/vendor/modernc.org/sqlite/vtab/vtab.go b/vendor/modernc.org/sqlite/vtab/vtab.go new file mode 100644 index 0000000..f003dcd --- /dev/null +++ b/vendor/modernc.org/sqlite/vtab/vtab.go @@ -0,0 +1,245 @@ +package vtab + +import ( + "database/sql" + "database/sql/driver" + "errors" +) + +// Value is the value type passed to and from virtual table cursors. It +// aliases database/sql/driver.Value to avoid exposing low-level details to +// module authors while remaining compatible with the driver. +type Value = driver.Value + +// Context carries information that a Module may need when creating or +// connecting a table instance. It intentionally does not expose *sql.DB to +// avoid leaking database/sql internals into the vtab API. Additional fields +// may be added in the future as needed. +type Context struct { + declare func(string) error +} + +// Declare must be called by a module from within Create or Connect to declare +// the schema of the virtual table. The provided SQL must be a CREATE TABLE +// statement describing the exposed columns. +// +// The engine installs this callback so that the declaration is executed in the +// correct context. Calling Declare outside of Create/Connect may fail. +func (c Context) Declare(schema string) error { + if c.declare == nil { + return errors.New("vtab: declare not available in this context") + } + return c.declare(schema) +} + +// NewContext is used by the engine to create a Context bound to the current +// xCreate/xConnect call. External modules should not need to call this. +func NewContext(declare func(string) error) Context { return Context{declare: declare} } + +// Module represents a virtual table module, analogous to sqlite3_module in +// the SQLite C API. Implementations are responsible for creating and +// connecting table instances. +type Module interface { + // Create is called to create a new virtual table. args corresponds to the + // argv array passed to xCreate in the SQLite C API: it contains the module + // name, the database name, the table name, and module arguments. + Create(ctx Context, args []string) (Table, error) + + // Connect is called to connect to an existing virtual table. Its + // semantics mirror xConnect in the SQLite C API. + Connect(ctx Context, args []string) (Table, error) +} + +// Table represents a single virtual table instance (the Go analogue of +// sqlite3_vtab and its associated methods). +type Table interface { + // BestIndex allows the virtual table to inform SQLite about which + // constraints and orderings it can efficiently support. The IndexInfo + // structure mirrors sqlite3_index_info. + BestIndex(info *IndexInfo) error + + // Open creates a new cursor for scanning the table. + Open() (Cursor, error) + + // Disconnect is called to disconnect from a table instance (xDisconnect). + Disconnect() error + + // Destroy is called when a table is dropped (xDestroy). + Destroy() error +} + +// Renamer can be implemented by a Table to handle xRename. +type Renamer interface { + Rename(newName string) error +} + +// Transactional can be implemented by a Table to handle transaction-related +// callbacks. Methods are optional; unimplemented methods are treated as no-op. +type Transactional interface { + Begin() error + Sync() error + Commit() error + Rollback() error + Savepoint(i int) error + Release(i int) error + RollbackTo(i int) error +} + +// Cursor represents a cursor over a virtual table (sqlite3_vtab_cursor). +type Cursor interface { + // Filter corresponds to xFilter. idxNum and idxStr are the chosen index + // number and string; vals are the constraint arguments. + Filter(idxNum int, idxStr string, vals []Value) error + + // Next advances the cursor to the next row (xNext). + Next() error + + // Eof reports whether the cursor is past the last row (xEof != 0). + Eof() bool + + // Column returns the value of the specified column in the current row + // (xColumn). + Column(col int) (Value, error) + + // Rowid returns the current rowid (xRowid). + Rowid() (int64, error) + + // Close closes the cursor (xClose). + Close() error +} + +// Updater can be implemented by a Table to support writes via xUpdate. +// +// Semantics follow SQLite's xUpdate: +// - Delete: Delete(oldRowid) is called. +// - Insert: Insert(cols, rowid) is called. *rowid may contain a desired rowid +// (if provided by SQL) and should be set to the final rowid of the new row. +// - Update: Update(oldRowid, cols, newRowid) is called. *newRowid may be set +// to the final rowid of the updated row when changed. +type Updater interface { + Insert(cols []Value, rowid *int64) error + Update(oldRowid int64, cols []Value, newRowid *int64) error + Delete(oldRowid int64) error +} + +// ConstraintOp describes the operator used in a constraint on a virtual +// table column. It loosely mirrors the op field of sqlite3_index_constraint. +type ConstraintOp int + +const ( + // OpUnknown indicates an operator that is not recognized or not mapped. + // Modules should treat this conservatively. + OpUnknown ConstraintOp = iota + OpEQ + OpGT + OpLE + OpLT + OpGE + OpMATCH // "MATCH" operator (e.g. for FTS or KNN semantics) + OpNE + OpIS + OpISNOT + OpISNULL + OpISNOTNULL + OpLIKE + OpGLOB + OpREGEXP + OpFUNCTION + OpLIMIT + OpOFFSET +) + +// Constraint describes a single WHERE-clause constraint that SQLite is +// considering pushing down to the virtual table. +type Constraint struct { + Column int + Op ConstraintOp + Usable bool + // ArgIndex selects which position in argv[] (0-based) should contain the + // RHS value for this constraint when Filter is called. Set to -1 to ignore. + ArgIndex int + // Omit requests SQLite to omit the corresponding constraint from the + // parent query if the virtual table fully handles it. + Omit bool +} + +// OrderBy describes a single ORDER BY term for a query involving a virtual +// table. +type OrderBy struct { + Column int + Desc bool +} + +// IndexInfo holds information about constraints and orderings for a virtual +// table query. It is the Go analogue of sqlite3_index_info. +type IndexInfo struct { + Constraints []Constraint + OrderBy []OrderBy + + // IdxNum selects the query plan chosen in BestIndex. This value is passed + // back to Cursor.Filter. Note: SQLite stores this as a 32-bit signed + // integer (int32). Implementations must ensure IdxNum fits within the + // int32 range; values outside of int32 will cause an error in the driver + // to avoid silent truncation. + IdxNum int64 + IdxStr string + // IdxFlags provides extra information about the chosen plan. + // Set to IndexScanUnique to indicate the plan visits at most one row. + IdxFlags int + OrderByConsumed bool + EstimatedCost float64 + EstimatedRows int64 + // ColUsed is a bitmask indicating which columns are used by the query. + // Bit N is set if column N is referenced. + ColUsed uint64 +} + +// Index flag values for IndexInfo.IdxFlags. +const ( + // IndexScanUnique mirrors SQLITE_INDEX_SCAN_UNIQUE and indicates that the + // chosen plan will visit at most one row. + IndexScanUnique = 1 +) + +// ErrNotImplemented is returned by RegisterModule when the underlying engine +// has not yet installed a registration hook. External projects can depend on +// the vtab API surface before the low-level bridge to sqlite3_create_module +// is fully wired; once the engine sets the hook via SetRegisterFunc, +// RegisterModule will forward calls to it. +var ErrNotImplemented = errors.New("vtab: RegisterModule not wired into engine") + +// registerHook is installed by the engine package via SetRegisterFunc. It is +// invoked by RegisterModule to perform the actual module registration. +var registerHook func(name string, m Module) error + +// SetRegisterFunc is intended to be called by the engine package to provide +// the concrete implementation of module registration. External callers +// should use RegisterModule instead. +func SetRegisterFunc(fn func(name string, m Module) error) { registerHook = fn } + +// RegisterModule registers a virtual table module with the provided *sql.DB. +// +// Registration applies to new connections only. Existing open connections will +// not be updated to include newly registered modules. +// +// Registration is performed when opening a new connection. If the underlying +// sqlite3_create_module_v2 call fails, opening the connection fails and returns +// that error. This fail-fast behavior prevents partially-initialized +// connections when a module cannot be installed. +// +// The db parameter is currently unused by the engine; it is available so +// module implementations can capture it if they need a *sql.DB for their own +// internal queries. +func RegisterModule(db *sql.DB, name string, m Module) error { + _ = db + if registerHook == nil { + return ErrNotImplemented + } + if name == "" { + return errors.New("vtab: module name must be non-empty") + } + if m == nil { + return errors.New("vtab: module implementation is nil") + } + return registerHook(name, m) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ae9fa64..5165383 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -24,7 +24,7 @@ github.com/buger/jsonparser # github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a ## explicit github.com/cention-sany/utf7 -# github.com/clipperhouse/displaywidth v0.6.1 +# github.com/clipperhouse/displaywidth v0.6.2 ## explicit; go 1.18 github.com/clipperhouse/displaywidth # github.com/clipperhouse/stringish v0.1.1 @@ -84,7 +84,7 @@ github.com/fatih/color ## explicit; go 1.17 github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify/internal -# github.com/gabriel-vasile/mimetype v1.4.11 +# github.com/gabriel-vasile/mimetype v1.4.12 ## explicit; go 1.21 github.com/gabriel-vasile/mimetype github.com/gabriel-vasile/mimetype/internal/charset @@ -241,7 +241,7 @@ github.com/yuin/goldmark/renderer github.com/yuin/goldmark/renderer/html github.com/yuin/goldmark/text github.com/yuin/goldmark/util -# go.mau.fi/util v0.9.3 +# go.mau.fi/util v0.9.4 ## explicit; go 1.24.0 go.mau.fi/util/base58 go.mau.fi/util/confusable @@ -276,7 +276,7 @@ golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/pbkdf2 golang.org/x/crypto/ssh golang.org/x/crypto/ssh/internal/bcrypt_pbkdf -# golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 +# golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 ## explicit; go 1.24.0 golang.org/x/exp/constraints golang.org/x/exp/maps @@ -321,7 +321,7 @@ golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width -# golang.org/x/tools v0.39.0 +# golang.org/x/tools v0.40.0 ## explicit; go 1.24.0 golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/buildutil @@ -330,7 +330,7 @@ golang.org/x/tools/go/loader # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# maunium.net/go/mautrix v0.26.0 +# maunium.net/go/mautrix v0.26.1 ## explicit; go 1.24.0 maunium.net/go/mautrix maunium.net/go/mautrix/crypto @@ -398,7 +398,8 @@ modernc.org/mathutil # modernc.org/memory v1.11.0 ## explicit; go 1.23.0 modernc.org/memory -# modernc.org/sqlite v1.40.1 +# modernc.org/sqlite v1.41.0 ## explicit; go 1.24.0 modernc.org/sqlite modernc.org/sqlite/lib +modernc.org/sqlite/vtab