summaryrefslogtreecommitdiffstats
path: root/vendor/maunium.net/go/mautrix/util
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/maunium.net/go/mautrix/util')
-rw-r--r--vendor/maunium.net/go/mautrix/util/base58/README.md9
-rw-r--r--vendor/maunium.net/go/mautrix/util/base58/alphabet.go49
-rw-r--r--vendor/maunium.net/go/mautrix/util/base58/base58.go138
-rw-r--r--vendor/maunium.net/go/mautrix/util/base58/base58check.go52
-rw-r--r--vendor/maunium.net/go/mautrix/util/base58/doc.go29
-rw-r--r--vendor/maunium.net/go/mautrix/util/dualerror.go33
-rw-r--r--vendor/maunium.net/go/mautrix/util/gjson.go31
-rw-r--r--vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go86
-rw-r--r--vendor/maunium.net/go/mautrix/util/marshal.go30
-rw-r--r--vendor/maunium.net/go/mautrix/util/mimetypes.go50
-rw-r--r--vendor/maunium.net/go/mautrix/util/random.go65
-rw-r--r--vendor/maunium.net/go/mautrix/util/returnonce.go23
-rw-r--r--vendor/maunium.net/go/mautrix/util/ringbuffer.go77
-rw-r--r--vendor/maunium.net/go/mautrix/util/syncmap.go94
14 files changed, 766 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/mautrix/util/base58/README.md b/vendor/maunium.net/go/mautrix/util/base58/README.md
new file mode 100644
index 00000000..50954507
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/base58/README.md
@@ -0,0 +1,9 @@
+base58
+==========
+
+This is a copy of <https://github.com/btcsuite/btcd/tree/master/btcutil/base58>.
+
+## License
+
+Package base58 is licensed under the [copyfree](http://copyfree.org) ISC
+License.
diff --git a/vendor/maunium.net/go/mautrix/util/base58/alphabet.go b/vendor/maunium.net/go/mautrix/util/base58/alphabet.go
new file mode 100644
index 00000000..6bb39fef
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/base58/alphabet.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2015 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+// AUTOGENERATED by genalphabet.go; do not edit.
+
+package base58
+
+const (
+ // alphabet is the modified base58 alphabet used by Bitcoin.
+ alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+
+ alphabetIdx0 = '1'
+)
+
+var b58 = [256]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 255, 255, 255, 255, 255, 255,
+ 255, 9, 10, 11, 12, 13, 14, 15,
+ 16, 255, 17, 18, 19, 20, 21, 255,
+ 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 255, 255, 255, 255, 255,
+ 255, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 255, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+}
diff --git a/vendor/maunium.net/go/mautrix/util/base58/base58.go b/vendor/maunium.net/go/mautrix/util/base58/base58.go
new file mode 100644
index 00000000..8ee59567
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/base58/base58.go
@@ -0,0 +1,138 @@
+// Copyright (c) 2013-2015 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package base58
+
+import (
+ "math/big"
+)
+
+//go:generate go run genalphabet.go
+
+var bigRadix = [...]*big.Int{
+ big.NewInt(0),
+ big.NewInt(58),
+ big.NewInt(58 * 58),
+ big.NewInt(58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
+ big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
+ bigRadix10,
+}
+
+var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
+
+// Decode decodes a modified base58 string to a byte slice.
+func Decode(b string) []byte {
+ answer := big.NewInt(0)
+ scratch := new(big.Int)
+
+ // Calculating with big.Int is slow for each iteration.
+ // x += b58[b[i]] * j
+ // j *= 58
+ //
+ // Instead we can try to do as much calculations on int64.
+ // We can represent a 10 digit base58 number using an int64.
+ //
+ // Hence we'll try to convert 10, base58 digits at a time.
+ // The rough idea is to calculate `t`, such that:
+ //
+ // t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
+ // x *= 58^10
+ // x += t
+ //
+ // Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
+ // In that case we'll use the bigRadix[n] lookup for the appropriate power.
+ for t := b; len(t) > 0; {
+ n := len(t)
+ if n > 10 {
+ n = 10
+ }
+
+ total := uint64(0)
+ for _, v := range t[:n] {
+ tmp := b58[v]
+ if tmp == 255 {
+ return []byte("")
+ }
+ total = total*58 + uint64(tmp)
+ }
+
+ answer.Mul(answer, bigRadix[n])
+ scratch.SetUint64(total)
+ answer.Add(answer, scratch)
+
+ t = t[n:]
+ }
+
+ tmpval := answer.Bytes()
+
+ var numZeros int
+ for numZeros = 0; numZeros < len(b); numZeros++ {
+ if b[numZeros] != alphabetIdx0 {
+ break
+ }
+ }
+ flen := numZeros + len(tmpval)
+ val := make([]byte, flen)
+ copy(val[numZeros:], tmpval)
+
+ return val
+}
+
+// Encode encodes a byte slice to a modified base58 string.
+func Encode(b []byte) string {
+ x := new(big.Int)
+ x.SetBytes(b)
+
+ // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
+ maxlen := int(float64(len(b))*1.365658237309761) + 1
+ answer := make([]byte, 0, maxlen)
+ mod := new(big.Int)
+ for x.Sign() > 0 {
+ // Calculating with big.Int is slow for each iteration.
+ // x, mod = x / 58, x % 58
+ //
+ // Instead we can try to do as much calculations on int64.
+ // x, mod = x / 58^10, x % 58^10
+ //
+ // Which will give us mod, which is 10 digit base58 number.
+ // We'll loop that 10 times to convert to the answer.
+
+ x.DivMod(x, bigRadix10, mod)
+ if x.Sign() == 0 {
+ // When x = 0, we need to ensure we don't add any extra zeros.
+ m := mod.Int64()
+ for m > 0 {
+ answer = append(answer, alphabet[m%58])
+ m /= 58
+ }
+ } else {
+ m := mod.Int64()
+ for i := 0; i < 10; i++ {
+ answer = append(answer, alphabet[m%58])
+ m /= 58
+ }
+ }
+ }
+
+ // leading zero bytes
+ for _, i := range b {
+ if i != 0 {
+ break
+ }
+ answer = append(answer, alphabetIdx0)
+ }
+
+ // reverse
+ alen := len(answer)
+ for i := 0; i < alen/2; i++ {
+ answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
+ }
+
+ return string(answer)
+}
diff --git a/vendor/maunium.net/go/mautrix/util/base58/base58check.go b/vendor/maunium.net/go/mautrix/util/base58/base58check.go
new file mode 100644
index 00000000..402c3233
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/base58/base58check.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2013-2014 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package base58
+
+import (
+ "crypto/sha256"
+ "errors"
+)
+
+// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
+// the checksum.
+var ErrChecksum = errors.New("checksum error")
+
+// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
+var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
+
+// checksum: first four bytes of sha256^2
+func checksum(input []byte) (cksum [4]byte) {
+ h := sha256.Sum256(input)
+ h2 := sha256.Sum256(h[:])
+ copy(cksum[:], h2[:4])
+ return
+}
+
+// CheckEncode prepends a version byte and appends a four byte checksum.
+func CheckEncode(input []byte, version byte) string {
+ b := make([]byte, 0, 1+len(input)+4)
+ b = append(b, version)
+ b = append(b, input...)
+ cksum := checksum(b)
+ b = append(b, cksum[:]...)
+ return Encode(b)
+}
+
+// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
+func CheckDecode(input string) (result []byte, version byte, err error) {
+ decoded := Decode(input)
+ if len(decoded) < 5 {
+ return nil, 0, ErrInvalidFormat
+ }
+ version = decoded[0]
+ var cksum [4]byte
+ copy(cksum[:], decoded[len(decoded)-4:])
+ if checksum(decoded[:len(decoded)-4]) != cksum {
+ return nil, 0, ErrChecksum
+ }
+ payload := decoded[1 : len(decoded)-4]
+ result = append(result, payload...)
+ return
+}
diff --git a/vendor/maunium.net/go/mautrix/util/base58/doc.go b/vendor/maunium.net/go/mautrix/util/base58/doc.go
new file mode 100644
index 00000000..d657f050
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/base58/doc.go
@@ -0,0 +1,29 @@
+// Copyright (c) 2014 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+/*
+Package base58 provides an API for working with modified base58 and Base58Check
+encodings.
+
+# Modified Base58 Encoding
+
+Standard base58 encoding is similar to standard base64 encoding except, as the
+name implies, it uses a 58 character alphabet which results in an alphanumeric
+string and allows some characters which are problematic for humans to be
+excluded. Due to this, there can be various base58 alphabets.
+
+The modified base58 alphabet used by Bitcoin, and hence this package, omits the
+0, O, I, and l characters that look the same in many fonts and are therefore
+hard to humans to distinguish.
+
+# Base58Check Encoding Scheme
+
+The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
+time of this writing, however it can be used to generically encode arbitrary
+byte arrays into human-readable strings along with a version byte that can be
+used to differentiate the same payload. For Bitcoin addresses, the extra
+version is used to differentiate the network of otherwise identical public keys
+which helps prevent using an address intended for one network on another.
+*/
+package base58
diff --git a/vendor/maunium.net/go/mautrix/util/dualerror.go b/vendor/maunium.net/go/mautrix/util/dualerror.go
new file mode 100644
index 00000000..b153d432
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/dualerror.go
@@ -0,0 +1,33 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import (
+ "errors"
+ "fmt"
+)
+
+type DualError struct {
+ High error
+ Low error
+}
+
+func NewDualError(high, low error) DualError {
+ return DualError{high, low}
+}
+
+func (err DualError) Is(other error) bool {
+ return errors.Is(other, err.High) || errors.Is(other, err.Low)
+}
+
+func (err DualError) Unwrap() error {
+ return err.Low
+}
+
+func (err DualError) Error() string {
+ return fmt.Sprintf("%v: %v", err.High, err.Low)
+}
diff --git a/vendor/maunium.net/go/mautrix/util/gjson.go b/vendor/maunium.net/go/mautrix/util/gjson.go
new file mode 100644
index 00000000..05ef8277
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/gjson.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import (
+ "strings"
+)
+
+var GJSONEscaper = strings.NewReplacer(
+ `\`, `\\`,
+ ".", `\.`,
+ "|", `\|`,
+ "#", `\#`,
+ "@", `\@`,
+ "*", `\*`,
+ "?", `\?`)
+
+func GJSONPath(path ...string) string {
+ var result strings.Builder
+ for i, part := range path {
+ _, _ = GJSONEscaper.WriteString(&result, part)
+ if i < len(path)-1 {
+ result.WriteRune('.')
+ }
+ }
+ return result.String()
+}
diff --git a/vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go b/vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go
new file mode 100644
index 00000000..b3282338
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/jsontime/jsontime.go
@@ -0,0 +1,86 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package jsontime
+
+import (
+ "encoding/json"
+ "time"
+)
+
+func UM(time time.Time) UnixMilli {
+ return UnixMilli{Time: time}
+}
+
+func UMInt(ts int64) UnixMilli {
+ return UM(time.UnixMilli(ts))
+}
+
+func UnixMilliNow() UnixMilli {
+ return UM(time.Now())
+}
+
+type UnixMilli struct {
+ time.Time
+}
+
+func (um UnixMilli) MarshalJSON() ([]byte, error) {
+ if um.IsZero() {
+ return []byte{'0'}, nil
+ }
+ return json.Marshal(um.UnixMilli())
+}
+
+func (um *UnixMilli) UnmarshalJSON(data []byte) error {
+ var val int64
+ err := json.Unmarshal(data, &val)
+ if err != nil {
+ return err
+ }
+ if val == 0 {
+ um.Time = time.Time{}
+ } else {
+ um.Time = time.UnixMilli(val)
+ }
+ return nil
+}
+
+func U(time time.Time) Unix {
+ return Unix{Time: time}
+}
+
+func UInt(ts int64) Unix {
+ return U(time.Unix(ts, 0))
+}
+
+func UnixNow() Unix {
+ return U(time.Now())
+}
+
+type Unix struct {
+ time.Time
+}
+
+func (u Unix) MarshalJSON() ([]byte, error) {
+ if u.IsZero() {
+ return []byte{'0'}, nil
+ }
+ return json.Marshal(u.Unix())
+}
+
+func (u *Unix) UnmarshalJSON(data []byte) error {
+ var val int64
+ err := json.Unmarshal(data, &val)
+ if err != nil {
+ return err
+ }
+ if val == 0 {
+ u.Time = time.Time{}
+ } else {
+ u.Time = time.Unix(val, 0)
+ }
+ return nil
+}
diff --git a/vendor/maunium.net/go/mautrix/util/marshal.go b/vendor/maunium.net/go/mautrix/util/marshal.go
new file mode 100644
index 00000000..180adef2
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/marshal.go
@@ -0,0 +1,30 @@
+package util
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/tidwall/gjson"
+ "github.com/tidwall/sjson"
+)
+
+// MarshalAndDeleteEmpty marshals a JSON object, then uses gjson to delete empty objects at the given gjson paths.
+//
+// This can be used as a convenient way to create a marshaler that omits empty non-pointer structs.
+// See mautrix.RespSync for example.
+func MarshalAndDeleteEmpty(marshalable interface{}, paths []string) ([]byte, error) {
+ data, err := json.Marshal(marshalable)
+ if err != nil {
+ return nil, err
+ }
+ for _, path := range paths {
+ res := gjson.GetBytes(data, path)
+ if res.IsObject() && len(res.Raw) == 2 {
+ data, err = sjson.DeleteBytes(data, path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to delete empty %s: %w", path, err)
+ }
+ }
+ }
+ return data, nil
+}
diff --git a/vendor/maunium.net/go/mautrix/util/mimetypes.go b/vendor/maunium.net/go/mautrix/util/mimetypes.go
new file mode 100644
index 00000000..9877c3d3
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/mimetypes.go
@@ -0,0 +1,50 @@
+// Copyright (c) 2022 Sumner Evans
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import (
+ "mime"
+ "strings"
+)
+
+// MimeExtensionSanityOverrides includes extensions for various common mimetypes.
+//
+// This is necessary because sometimes the OS mimetype database and Go interact in weird ways,
+// which causes very obscure extensions to be first in the array for common mimetypes
+// (e.g. image/jpeg -> .jpe, text/plain -> ,v).
+var MimeExtensionSanityOverrides = map[string]string{
+ "image/png": ".png",
+ "image/webp": ".webp",
+ "image/jpeg": ".jpg",
+ "image/tiff": ".tiff",
+ "image/heif": ".heic",
+ "image/heic": ".heic",
+
+ "audio/mpeg": ".mp3",
+ "audio/ogg": ".ogg",
+ "audio/webm": ".webm",
+ "audio/x-caf": ".caf",
+ "video/mp4": ".mp4",
+ "video/mpeg": ".mpeg",
+ "video/webm": ".webm",
+
+ "text/plain": ".txt",
+ "text/html": ".html",
+
+ "application/xml": ".xml",
+}
+
+func ExtensionFromMimetype(mimetype string) string {
+ ext, ok := MimeExtensionSanityOverrides[strings.Split(mimetype, ";")[0]]
+ if !ok {
+ exts, _ := mime.ExtensionsByType(mimetype)
+ if len(exts) > 0 {
+ ext = exts[0]
+ }
+ }
+ return ext
+}
diff --git a/vendor/maunium.net/go/mautrix/util/random.go b/vendor/maunium.net/go/mautrix/util/random.go
new file mode 100644
index 00000000..944dd034
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/random.go
@@ -0,0 +1,65 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "hash/crc32"
+ "strings"
+ "unsafe"
+)
+
+func RandomBytes(n int) []byte {
+ data := make([]byte, n)
+ _, err := rand.Read(data)
+ if err != nil {
+ panic(err)
+ }
+ return data
+}
+
+var letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+// RandomString generates a random string of the given length.
+func RandomString(n int) string {
+ if n <= 0 {
+ return ""
+ }
+ base64Len := n
+ if n%4 != 0 {
+ base64Len += 4 - (n % 4)
+ }
+ decodedLength := base64.RawStdEncoding.DecodedLen(base64Len)
+ output := make([]byte, base64Len)
+ base64.RawStdEncoding.Encode(output, RandomBytes(decodedLength))
+ for i, char := range output {
+ if char == '+' || char == '/' {
+ _, err := rand.Read(output[i : i+1])
+ if err != nil {
+ panic(err)
+ }
+ output[i] = letters[int(output[i])%len(letters)]
+ }
+ }
+ return (*(*string)(unsafe.Pointer(&output)))[:n]
+}
+
+func base62Encode(val uint32, minWidth int) string {
+ var buf strings.Builder
+ for val > 0 {
+ buf.WriteByte(letters[val%62])
+ val /= 62
+ }
+ return strings.Repeat("0", minWidth-buf.Len()) + buf.String()
+}
+
+func RandomToken(namespace string, randomLength int) string {
+ token := namespace + "_" + RandomString(randomLength)
+ checksum := base62Encode(crc32.ChecksumIEEE([]byte(token)), 6)
+ return token + "_" + checksum
+}
diff --git a/vendor/maunium.net/go/mautrix/util/returnonce.go b/vendor/maunium.net/go/mautrix/util/returnonce.go
new file mode 100644
index 00000000..c85aae0a
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/returnonce.go
@@ -0,0 +1,23 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import "sync"
+
+// ReturnableOnce is a wrapper for sync.Once that can return a value
+type ReturnableOnce[Value any] struct {
+ once sync.Once
+ output Value
+ err error
+}
+
+func (ronce *ReturnableOnce[Value]) Do(fn func() (Value, error)) (Value, error) {
+ ronce.once.Do(func() {
+ ronce.output, ronce.err = fn()
+ })
+ return ronce.output, ronce.err
+}
diff --git a/vendor/maunium.net/go/mautrix/util/ringbuffer.go b/vendor/maunium.net/go/mautrix/util/ringbuffer.go
new file mode 100644
index 00000000..ddb4c79f
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/ringbuffer.go
@@ -0,0 +1,77 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import (
+ "sync"
+)
+
+type pair[Key comparable, Value any] struct {
+ Key Key
+ Value Value
+}
+
+type RingBuffer[Key comparable, Value any] struct {
+ ptr int
+ data []pair[Key, Value]
+ lock sync.RWMutex
+}
+
+func NewRingBuffer[Key comparable, Value any](size int) *RingBuffer[Key, Value] {
+ return &RingBuffer[Key, Value]{
+ data: make([]pair[Key, Value], size),
+ }
+}
+
+func (rb *RingBuffer[Key, Value]) Contains(val Key) bool {
+ _, ok := rb.Get(val)
+ return ok
+}
+
+func (rb *RingBuffer[Key, Value]) Get(key Key) (val Value, found bool) {
+ rb.lock.RLock()
+ end := rb.ptr
+ for i := clamp(end-1, len(rb.data)); i != end; i = clamp(i-1, len(rb.data)) {
+ if rb.data[i].Key == key {
+ val = rb.data[i].Value
+ found = true
+ break
+ }
+ }
+ rb.lock.RUnlock()
+ return
+}
+
+func (rb *RingBuffer[Key, Value]) Replace(key Key, val Value) bool {
+ rb.lock.Lock()
+ defer rb.lock.Unlock()
+ end := rb.ptr
+ for i := clamp(end-1, len(rb.data)); i != end; i = clamp(i-1, len(rb.data)) {
+ if rb.data[i].Key == key {
+ rb.data[i].Value = val
+ return true
+ }
+ }
+ return false
+}
+
+func (rb *RingBuffer[Key, Value]) Push(key Key, val Value) {
+ rb.lock.Lock()
+ rb.data[rb.ptr] = pair[Key, Value]{Key: key, Value: val}
+ rb.ptr = (rb.ptr + 1) % len(rb.data)
+ rb.lock.Unlock()
+}
+
+func clamp(index, len int) int {
+ if index < 0 {
+ return len + index
+ } else if index >= len {
+ return len - index
+ } else {
+ return index
+ }
+}
diff --git a/vendor/maunium.net/go/mautrix/util/syncmap.go b/vendor/maunium.net/go/mautrix/util/syncmap.go
new file mode 100644
index 00000000..a6b20d3b
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/util/syncmap.go
@@ -0,0 +1,94 @@
+// Copyright (c) 2023 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package util
+
+import "sync"
+
+// SyncMap is a simple map with a built-in mutex.
+type SyncMap[Key comparable, Value any] struct {
+ data map[Key]Value
+ lock sync.RWMutex
+}
+
+func NewSyncMap[Key comparable, Value any]() *SyncMap[Key, Value] {
+ return &SyncMap[Key, Value]{
+ data: make(map[Key]Value),
+ }
+}
+
+// Set stores a value in the map.
+func (sm *SyncMap[Key, Value]) Set(key Key, value Value) {
+ sm.Swap(key, value)
+}
+
+// Swap sets a value in the map and returns the old value.
+//
+// The boolean return parameter is true if the value already existed, false if not.
+func (sm *SyncMap[Key, Value]) Swap(key Key, value Value) (oldValue Value, wasReplaced bool) {
+ sm.lock.Lock()
+ oldValue, wasReplaced = sm.data[key]
+ sm.data[key] = value
+ sm.lock.Unlock()
+ return
+}
+
+// Delete removes a key from the map.
+func (sm *SyncMap[Key, Value]) Delete(key Key) {
+ sm.Pop(key)
+}
+
+// Pop removes a key from the map and returns the old value.
+//
+// The boolean return parameter is the same as with normal Go map access (true if the key exists, false if not).
+func (sm *SyncMap[Key, Value]) Pop(key Key) (value Value, ok bool) {
+ sm.lock.Lock()
+ value, ok = sm.data[key]
+ delete(sm.data, key)
+ sm.lock.Unlock()
+ return
+}
+
+// Get gets a value in the map.
+//
+// The boolean return parameter is the same as with normal Go map access (true if the key exists, false if not).
+func (sm *SyncMap[Key, Value]) Get(key Key) (value Value, ok bool) {
+ sm.lock.RLock()
+ value, ok = sm.data[key]
+ sm.lock.RUnlock()
+ return
+}
+
+// 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.
+func (sm *SyncMap[Key, Value]) GetOrSet(key Key, value Value) (actual Value, wasGet bool) {
+ sm.lock.Lock()
+ defer sm.lock.Unlock()
+ actual, wasGet = sm.data[key]
+ if wasGet {
+ return
+ }
+ sm.data[key] = value
+ actual = value
+ return
+}
+
+// Clone returns a copy of the map.
+func (sm *SyncMap[Key, Value]) Clone() *SyncMap[Key, Value] {
+ return &SyncMap[Key, Value]{data: sm.CopyData()}
+}
+
+// CopyData returns a copy of the data in the map as a normal (non-atomic) map.
+func (sm *SyncMap[Key, Value]) CopyData() map[Key]Value {
+ sm.lock.RLock()
+ copied := make(map[Key]Value, len(sm.data))
+ for key, value := range sm.data {
+ copied[key] = value
+ }
+ sm.lock.RUnlock()
+ return copied
+}