mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 08:23:48 +03:00
replace packages slog and slices from golang.org/x/exp with stdlib
since we are now at go1.21 as minimum.
This commit is contained in:
parent
c698cd07d9
commit
d1b87cdb0d
123 changed files with 191 additions and 4860 deletions
76
apidiff/v0.0.10.txt
Normal file
76
apidiff/v0.0.10.txt
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
Below are the incompatible changes between v0.0.9 and v0.0.10, per package.
|
||||||
|
|
||||||
|
# dane
|
||||||
|
- Dial: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, string, string, []github.com/mjl-/adns.TLSAUsage, *crypto/x509.CertPool) (net.Conn, github.com/mjl-/adns.TLSA, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, string, string, []github.com/mjl-/adns.TLSAUsage, *crypto/x509.CertPool) (net.Conn, github.com/mjl-/adns.TLSA, error)
|
||||||
|
- TLSClientConfig: changed from func(*golang.org/x/exp/slog.Logger, []github.com/mjl-/adns.TLSA, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.Domain, *github.com/mjl-/adns.TLSA, *crypto/x509.CertPool) crypto/tls.Config to func(*log/slog.Logger, []github.com/mjl-/adns.TLSA, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.Domain, *github.com/mjl-/adns.TLSA, *crypto/x509.CertPool) crypto/tls.Config
|
||||||
|
- Verify: changed from func(*golang.org/x/exp/slog.Logger, []github.com/mjl-/adns.TLSA, crypto/tls.ConnectionState, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.Domain, *crypto/x509.CertPool) (bool, github.com/mjl-/adns.TLSA, error) to func(*log/slog.Logger, []github.com/mjl-/adns.TLSA, crypto/tls.ConnectionState, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.Domain, *crypto/x509.CertPool) (bool, github.com/mjl-/adns.TLSA, error)
|
||||||
|
|
||||||
|
# dmarc
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Status, github.com/mjl-/mox/dns.Domain, *Record, string, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Status, github.com/mjl-/mox/dns.Domain, *Record, string, bool, error)
|
||||||
|
- LookupExternalReportsAccepted: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, github.com/mjl-/mox/dns.Domain) (bool, Status, []*Record, []string, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, github.com/mjl-/mox/dns.Domain) (bool, Status, []*Record, []string, bool, error)
|
||||||
|
- Verify: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dkim.Result, github.com/mjl-/mox/spf.Status, *github.com/mjl-/mox/dns.Domain, bool) (bool, Result) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dkim.Result, github.com/mjl-/mox/spf.Status, *github.com/mjl-/mox/dns.Domain, bool) (bool, Result)
|
||||||
|
|
||||||
|
# dmarcrpt
|
||||||
|
- ParseMessageReport: changed from func(*golang.org/x/exp/slog.Logger, io.ReaderAt) (*Feedback, error) to func(*log/slog.Logger, io.ReaderAt) (*Feedback, error)
|
||||||
|
|
||||||
|
# dns
|
||||||
|
- StrictResolver.Log: changed from *golang.org/x/exp/slog.Logger to *log/slog.Logger
|
||||||
|
|
||||||
|
# dnsbl
|
||||||
|
- CheckHealth: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) error to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) error
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, net.IP) (Status, string, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, net.IP) (Status, string, error)
|
||||||
|
|
||||||
|
# iprev
|
||||||
|
|
||||||
|
# message
|
||||||
|
- (*Part).ParseNextPart: changed from func(*golang.org/x/exp/slog.Logger) (*Part, error) to func(*log/slog.Logger) (*Part, error)
|
||||||
|
- (*Part).Walk: changed from func(*golang.org/x/exp/slog.Logger, *Part) error to func(*log/slog.Logger, *Part) error
|
||||||
|
- EnsurePart: changed from func(*golang.org/x/exp/slog.Logger, bool, io.ReaderAt, int64) (Part, error) to func(*log/slog.Logger, bool, io.ReaderAt, int64) (Part, error)
|
||||||
|
- From: changed from func(*golang.org/x/exp/slog.Logger, bool, io.ReaderAt) (github.com/mjl-/mox/smtp.Address, *Envelope, net/textproto.MIMEHeader, error) to func(*log/slog.Logger, bool, io.ReaderAt) (github.com/mjl-/mox/smtp.Address, *Envelope, net/textproto.MIMEHeader, error)
|
||||||
|
- Parse: changed from func(*golang.org/x/exp/slog.Logger, bool, io.ReaderAt) (Part, error) to func(*log/slog.Logger, bool, io.ReaderAt) (Part, error)
|
||||||
|
|
||||||
|
# mtasts
|
||||||
|
- FetchPolicy: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Domain) (*Policy, string, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Domain) (*Policy, string, error)
|
||||||
|
- Get: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, *Policy, string, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, *Policy, string, error)
|
||||||
|
- HTTPClientObserve: changed from func(context.Context, *golang.org/x/exp/slog.Logger, string, string, int, error, time.Time) to func(context.Context, *log/slog.Logger, string, string, int, error, time.Time)
|
||||||
|
- LookupRecord: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, string, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, string, error)
|
||||||
|
|
||||||
|
# publicsuffix
|
||||||
|
- List.Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Domain) github.com/mjl-/mox/dns.Domain to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Domain) github.com/mjl-/mox/dns.Domain
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Domain) github.com/mjl-/mox/dns.Domain to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Domain) github.com/mjl-/mox/dns.Domain
|
||||||
|
- ParseList: changed from func(*golang.org/x/exp/slog.Logger, io.Reader) (List, error) to func(*log/slog.Logger, io.Reader) (List, error)
|
||||||
|
|
||||||
|
# ratelimit
|
||||||
|
|
||||||
|
# sasl
|
||||||
|
|
||||||
|
# scram
|
||||||
|
|
||||||
|
# smtp
|
||||||
|
|
||||||
|
# smtpclient
|
||||||
|
- Dial: changed from func(context.Context, *golang.org/x/exp/slog.Logger, Dialer, github.com/mjl-/mox/dns.IPDomain, []net.IP, int, map[string][]net.IP, []net.IP) (net.Conn, net.IP, error) to func(context.Context, *log/slog.Logger, Dialer, github.com/mjl-/mox/dns.IPDomain, []net.IP, int, map[string][]net.IP, []net.IP) (net.Conn, net.IP, error)
|
||||||
|
- GatherDestinations: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.IPDomain) (bool, bool, bool, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.IPDomain, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.IPDomain) (bool, bool, bool, github.com/mjl-/mox/dns.Domain, []github.com/mjl-/mox/dns.IPDomain, bool, error)
|
||||||
|
- GatherIPs: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.IPDomain, map[string][]net.IP) (bool, bool, github.com/mjl-/mox/dns.Domain, []net.IP, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.IPDomain, map[string][]net.IP) (bool, bool, github.com/mjl-/mox/dns.Domain, []net.IP, bool, error)
|
||||||
|
- GatherTLSA: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, bool, github.com/mjl-/mox/dns.Domain) (bool, []github.com/mjl-/adns.TLSA, github.com/mjl-/mox/dns.Domain, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, bool, github.com/mjl-/mox/dns.Domain) (bool, []github.com/mjl-/adns.TLSA, github.com/mjl-/mox/dns.Domain, error)
|
||||||
|
- New: changed from func(context.Context, *golang.org/x/exp/slog.Logger, net.Conn, TLSMode, bool, github.com/mjl-/mox/dns.Domain, github.com/mjl-/mox/dns.Domain, Opts) (*Client, error) to func(context.Context, *log/slog.Logger, net.Conn, TLSMode, bool, github.com/mjl-/mox/dns.Domain, github.com/mjl-/mox/dns.Domain, Opts) (*Client, error)
|
||||||
|
|
||||||
|
# spf
|
||||||
|
- Evaluate: changed from func(context.Context, *golang.org/x/exp/slog.Logger, *Record, github.com/mjl-/mox/dns.Resolver, Args) (Status, string, string, bool, error) to func(context.Context, *log/slog.Logger, *Record, github.com/mjl-/mox/dns.Resolver, Args) (Status, string, string, bool, error)
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Status, string, *Record, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Status, string, *Record, bool, error)
|
||||||
|
- Verify: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, Args) (Received, github.com/mjl-/mox/dns.Domain, string, bool, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, Args) (Received, github.com/mjl-/mox/dns.Domain, string, bool, error)
|
||||||
|
|
||||||
|
# subjectpass
|
||||||
|
- Generate: changed from func(*golang.org/x/exp/slog.Logger, github.com/mjl-/mox/smtp.Address, []byte, time.Time) string to func(*log/slog.Logger, github.com/mjl-/mox/smtp.Address, []byte, time.Time) string
|
||||||
|
- Verify: changed from func(*golang.org/x/exp/slog.Logger, io.ReaderAt, []byte, time.Duration) error to func(*log/slog.Logger, io.ReaderAt, []byte, time.Duration) error
|
||||||
|
|
||||||
|
# tlsrpt
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, string, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (*Record, string, error)
|
||||||
|
- ParseMessage: changed from func(*golang.org/x/exp/slog.Logger, io.ReaderAt) (*ReportJSON, error) to func(*log/slog.Logger, io.ReaderAt) (*ReportJSON, error)
|
||||||
|
|
||||||
|
# updates
|
||||||
|
- Check: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, Version, string, []byte) (Version, *Record, *Changelog, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain, Version, string, []byte) (Version, *Record, *Changelog, error)
|
||||||
|
- FetchChangelog: changed from func(context.Context, *golang.org/x/exp/slog.Logger, string, Version, []byte) (*Changelog, error) to func(context.Context, *log/slog.Logger, string, Version, []byte) (*Changelog, error)
|
||||||
|
- HTTPClientObserve: changed from func(context.Context, *golang.org/x/exp/slog.Logger, string, string, int, error, time.Time) to func(context.Context, *log/slog.Logger, string, string, int, error, time.Time)
|
||||||
|
- Lookup: changed from func(context.Context, *golang.org/x/exp/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Version, *Record, error) to func(context.Context, *log/slog.Logger, github.com/mjl-/mox/dns.Resolver, github.com/mjl-/mox/dns.Domain) (Version, *Record, error)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -29,7 +30,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
|
@ -7,13 +7,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dmarcdb"
|
"github.com/mjl-/mox/dmarcdb"
|
||||||
|
|
3
ctl.go
3
ctl.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -16,8 +17,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -55,12 +55,11 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -20,8 +21,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -4,8 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/moxio"
|
"github.com/mjl-/mox/moxio"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -23,8 +24,6 @@ import (
|
||||||
"github.com/mjl-/mox/publicsuffix"
|
"github.com/mjl-/mox/publicsuffix"
|
||||||
"github.com/mjl-/mox/spf"
|
"github.com/mjl-/mox/spf"
|
||||||
"github.com/mjl-/mox/stub"
|
"github.com/mjl-/mox/stub"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -3,11 +3,10 @@ package dmarc_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dkim"
|
"github.com/mjl-/mox/dkim"
|
||||||
"github.com/mjl-/mox/dmarc"
|
"github.com/mjl-/mox/dmarc"
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
@ -16,14 +17,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -13,8 +14,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dmarcrpt"
|
"github.com/mjl-/mox/dmarcrpt"
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -9,11 +9,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/moxio"
|
"github.com/mjl-/mox/moxio"
|
||||||
|
|
|
@ -4,8 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"slices"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,14 +4,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -18,13 +18,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/stub"
|
"github.com/mjl-/mox/stub"
|
||||||
|
|
|
@ -3,10 +3,9 @@ package dnsbl_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/dnsbl"
|
"github.com/mjl-/mox/dnsbl"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,13 +4,12 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/mjl-/mox
|
module github.com/mjl-/mox
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mjl-/adns v0.0.0-20231109160910-82839fe3e6ae
|
github.com/mjl-/adns v0.0.0-20231109160910-82839fe3e6ae
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -17,6 +17,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
@ -66,6 +67,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
@ -87,6 +89,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -109,5 +112,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||||
|
|
|
@ -3,11 +3,10 @@ package http
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"rsc.io/qr"
|
"rsc.io/qr"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -16,8 +17,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mox-"
|
"github.com/mjl-/mox/mox-"
|
||||||
"github.com/mjl-/mox/smtp"
|
"github.com/mjl-/mox/smtp"
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/mox-"
|
"github.com/mjl-/mox/mox-"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
golog "log"
|
golog "log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
golog "log"
|
golog "log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
@ -25,8 +26,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/config"
|
"github.com/mjl-/mox/config"
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -7,12 +7,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@ package imapserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
|
|
|
@ -44,6 +44,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -51,14 +52,13 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -16,7 +17,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/config"
|
"github.com/mjl-/mox/config"
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
|
|
|
@ -7,6 +7,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -14,8 +15,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/imapclient"
|
"github.com/mjl-/mox/imapclient"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -6,11 +6,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/stub"
|
"github.com/mjl-/mox/stub"
|
||||||
|
|
|
@ -15,14 +15,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
golog "log"
|
golog "log"
|
||||||
|
"log/slog"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/sconf"
|
"github.com/mjl-/sconf"
|
||||||
|
|
||||||
|
|
4
main.go
4
main.go
|
@ -21,18 +21,18 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
"github.com/mjl-/mox/smtp"
|
"github.com/mjl-/mox/smtp"
|
||||||
|
|
|
@ -3,10 +3,9 @@ package message
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/smtp"
|
"github.com/mjl-/mox/smtp"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/quotedprintable"
|
"mime/quotedprintable"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
"golang.org/x/text/encoding/ianaindex"
|
"golang.org/x/text/encoding/ianaindex"
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -4,11 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,13 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var noctx = context.Background()
|
var noctx = context.Background()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -20,7 +21,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -29,7 +30,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
|
|
||||||
"github.com/mjl-/autocert"
|
"github.com/mjl-/autocert"
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
package mox
|
package mox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fork and exec as unprivileged user.
|
// Fork and exec as unprivileged user.
|
||||||
|
|
|
@ -3,8 +3,7 @@ package mox
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,14 +7,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/moxvar"
|
"github.com/mjl-/mox/moxvar"
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,8 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,10 +3,9 @@ package moxio
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ package moxio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,8 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mtasts"
|
"github.com/mjl-/mox/mtasts"
|
||||||
|
|
|
@ -16,12 +16,11 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
golog "log"
|
golog "log"
|
||||||
|
"log/slog"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -18,8 +19,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -10,14 +10,13 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -3,8 +3,7 @@ package publicsuffix_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/publicsuffix"
|
"github.com/mjl-/mox/publicsuffix"
|
||||||
|
|
|
@ -14,11 +14,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
"golang.org/x/net/idna"
|
"golang.org/x/net/idna"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -6,13 +6,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,10 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -15,7 +16,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
|
@ -7,13 +7,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/config"
|
"github.com/mjl-/mox/config"
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/dsn"
|
"github.com/mjl-/mox/dsn"
|
||||||
|
|
|
@ -11,14 +11,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
htmltemplate "html/template"
|
htmltemplate "html/template"
|
||||||
"log"
|
"log"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var destdir string
|
var destdir string
|
||||||
|
|
|
@ -12,11 +12,10 @@ import (
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/mjl-/sconf"
|
"github.com/mjl-/sconf"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
cryptorand "crypto/rand"
|
cryptorand "crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -18,8 +19,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -35,14 +35,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dane"
|
"github.com/mjl-/mox/dane"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -21,8 +22,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/sasl"
|
"github.com/mjl-/mox/sasl"
|
||||||
|
|
|
@ -3,11 +3,10 @@ package smtpclient
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,12 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/sasl"
|
"github.com/mjl-/mox/sasl"
|
||||||
"github.com/mjl-/mox/smtpclient"
|
"github.com/mjl-/mox/smtpclient"
|
||||||
|
|
|
@ -7,13 +7,12 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -3,13 +3,12 @@ package smtpserver
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dkim"
|
"github.com/mjl-/mox/dkim"
|
||||||
|
|
|
@ -6,10 +6,9 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
|
|
|
@ -3,10 +3,9 @@ package smtpserver
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
@ -27,7 +28,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"math/big"
|
"math/big"
|
||||||
"mime/quotedprintable"
|
"mime/quotedprintable"
|
||||||
"net"
|
"net"
|
||||||
|
@ -23,8 +24,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/config"
|
"github.com/mjl-/mox/config"
|
||||||
|
|
|
@ -3,10 +3,9 @@ package spf_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/smtp"
|
"github.com/mjl-/mox/smtp"
|
||||||
"github.com/mjl-/mox/spf"
|
"github.com/mjl-/mox/spf"
|
||||||
|
|
|
@ -10,14 +10,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/smtp"
|
"github.com/mjl-/mox/smtp"
|
||||||
|
|
|
@ -33,9 +33,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -43,8 +45,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -13,7 +14,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,12 +6,11 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/metrics"
|
"github.com/mjl-/mox/metrics"
|
||||||
|
|
|
@ -6,13 +6,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
|
|
|
@ -4,11 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/config"
|
"github.com/mjl-/mox/config"
|
||||||
|
|
|
@ -2,9 +2,8 @@ package stub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HTTPClientObserveIgnore(ctx context.Context, log *slog.Logger, pkg, method string, statusCode int, err error, start time.Time) {
|
func HTTPClientObserveIgnore(ctx context.Context, log *slog.Logger, pkg, method string, statusCode int, err error, start time.Time) {
|
||||||
|
|
|
@ -14,11 +14,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/message"
|
"github.com/mjl-/mox/message"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
|
|
|
@ -3,10 +3,9 @@ package tlsrpt_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/tlsrpt"
|
"github.com/mjl-/mox/tlsrpt"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,10 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/stub"
|
"github.com/mjl-/mox/stub"
|
||||||
|
|
|
@ -9,16 +9,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/adns"
|
"github.com/mjl-/adns"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -4,12 +4,11 @@ package tlsrptdb
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -19,19 +19,18 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -11,8 +12,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
|
|
|
@ -16,13 +16,12 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/exp/slog"
|
|
||||||
|
|
||||||
"github.com/mjl-/mox/dns"
|
"github.com/mjl-/mox/dns"
|
||||||
"github.com/mjl-/mox/mlog"
|
"github.com/mjl-/mox/mlog"
|
||||||
"github.com/mjl-/mox/moxio"
|
"github.com/mjl-/mox/moxio"
|
||||||
|
|
50
vendor/golang.org/x/exp/constraints/constraints.go
generated
vendored
50
vendor/golang.org/x/exp/constraints/constraints.go
generated
vendored
|
@ -1,50 +0,0 @@
|
||||||
// Copyright 2021 The Go 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 constraints defines a set of useful constraints to be used
|
|
||||||
// with type parameters.
|
|
||||||
package constraints
|
|
||||||
|
|
||||||
// Signed is a constraint that permits any signed integer type.
|
|
||||||
// If future releases of Go add new predeclared signed integer types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Signed interface {
|
|
||||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsigned is a constraint that permits any unsigned integer type.
|
|
||||||
// If future releases of Go add new predeclared unsigned integer types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Unsigned interface {
|
|
||||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer is a constraint that permits any integer type.
|
|
||||||
// If future releases of Go add new predeclared integer types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Integer interface {
|
|
||||||
Signed | Unsigned
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float is a constraint that permits any floating-point type.
|
|
||||||
// If future releases of Go add new predeclared floating-point types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Float interface {
|
|
||||||
~float32 | ~float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complex is a constraint that permits any complex numeric type.
|
|
||||||
// If future releases of Go add new predeclared complex numeric types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Complex interface {
|
|
||||||
~complex64 | ~complex128
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ordered is a constraint that permits any ordered type: any type
|
|
||||||
// that supports the operators < <= >= >.
|
|
||||||
// If future releases of Go add new ordered types,
|
|
||||||
// this constraint will be modified to include them.
|
|
||||||
type Ordered interface {
|
|
||||||
Integer | Float | ~string
|
|
||||||
}
|
|
44
vendor/golang.org/x/exp/slices/cmp.go
generated
vendored
44
vendor/golang.org/x/exp/slices/cmp.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
// Copyright 2023 The Go 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 slices
|
|
||||||
|
|
||||||
import "golang.org/x/exp/constraints"
|
|
||||||
|
|
||||||
// min is a version of the predeclared function from the Go 1.21 release.
|
|
||||||
func min[T constraints.Ordered](a, b T) T {
|
|
||||||
if a < b || isNaN(a) {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// max is a version of the predeclared function from the Go 1.21 release.
|
|
||||||
func max[T constraints.Ordered](a, b T) T {
|
|
||||||
if a > b || isNaN(a) {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmpLess is a copy of cmp.Less from the Go 1.21 release.
|
|
||||||
func cmpLess[T constraints.Ordered](x, y T) bool {
|
|
||||||
return (isNaN(x) && !isNaN(y)) || x < y
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmpCompare is a copy of cmp.Compare from the Go 1.21 release.
|
|
||||||
func cmpCompare[T constraints.Ordered](x, y T) int {
|
|
||||||
xNaN := isNaN(x)
|
|
||||||
yNaN := isNaN(y)
|
|
||||||
if xNaN && yNaN {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if xNaN || x < y {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if yNaN || x > y {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
499
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
499
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
|
@ -1,499 +0,0 @@
|
||||||
// Copyright 2021 The Go 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 slices defines various functions useful with slices of any type.
|
|
||||||
package slices
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Equal reports whether two slices are equal: the same length and all
|
|
||||||
// elements equal. If the lengths are different, Equal returns false.
|
|
||||||
// Otherwise, the elements are compared in increasing index order, and the
|
|
||||||
// comparison stops at the first unequal pair.
|
|
||||||
// Floating point NaNs are not considered equal.
|
|
||||||
func Equal[S ~[]E, E comparable](s1, s2 S) bool {
|
|
||||||
if len(s1) != len(s2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range s1 {
|
|
||||||
if s1[i] != s2[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualFunc reports whether two slices are equal using an equality
|
|
||||||
// function on each pair of elements. If the lengths are different,
|
|
||||||
// EqualFunc returns false. Otherwise, the elements are compared in
|
|
||||||
// increasing index order, and the comparison stops at the first index
|
|
||||||
// for which eq returns false.
|
|
||||||
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool {
|
|
||||||
if len(s1) != len(s2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, v1 := range s1 {
|
|
||||||
v2 := s2[i]
|
|
||||||
if !eq(v1, v2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair
|
|
||||||
// of elements. The elements are compared sequentially, starting at index 0,
|
|
||||||
// until one element is not equal to the other.
|
|
||||||
// The result of comparing the first non-matching elements is returned.
|
|
||||||
// If both slices are equal until one of them ends, the shorter slice is
|
|
||||||
// considered less than the longer one.
|
|
||||||
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
|
|
||||||
func Compare[S ~[]E, E constraints.Ordered](s1, s2 S) int {
|
|
||||||
for i, v1 := range s1 {
|
|
||||||
if i >= len(s2) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
v2 := s2[i]
|
|
||||||
if c := cmpCompare(v1, v2); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s1) < len(s2) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareFunc is like [Compare] but uses a custom comparison function on each
|
|
||||||
// pair of elements.
|
|
||||||
// The result is the first non-zero result of cmp; if cmp always
|
|
||||||
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
|
|
||||||
// and +1 if len(s1) > len(s2).
|
|
||||||
func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int {
|
|
||||||
for i, v1 := range s1 {
|
|
||||||
if i >= len(s2) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
v2 := s2[i]
|
|
||||||
if c := cmp(v1, v2); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s1) < len(s2) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index returns the index of the first occurrence of v in s,
|
|
||||||
// or -1 if not present.
|
|
||||||
func Index[S ~[]E, E comparable](s S, v E) int {
|
|
||||||
for i := range s {
|
|
||||||
if v == s[i] {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexFunc returns the first index i satisfying f(s[i]),
|
|
||||||
// or -1 if none do.
|
|
||||||
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
|
||||||
for i := range s {
|
|
||||||
if f(s[i]) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains reports whether v is present in s.
|
|
||||||
func Contains[S ~[]E, E comparable](s S, v E) bool {
|
|
||||||
return Index(s, v) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsFunc reports whether at least one
|
|
||||||
// element e of s satisfies f(e).
|
|
||||||
func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
|
|
||||||
return IndexFunc(s, f) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert inserts the values v... into s at index i,
|
|
||||||
// returning the modified slice.
|
|
||||||
// The elements at s[i:] are shifted up to make room.
|
|
||||||
// In the returned slice r, r[i] == v[0],
|
|
||||||
// and r[i+len(v)] == value originally at r[i].
|
|
||||||
// Insert panics if i is out of range.
|
|
||||||
// This function is O(len(s) + len(v)).
|
|
||||||
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
|
||||||
m := len(v)
|
|
||||||
if m == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
n := len(s)
|
|
||||||
if i == n {
|
|
||||||
return append(s, v...)
|
|
||||||
}
|
|
||||||
if n+m > cap(s) {
|
|
||||||
// Use append rather than make so that we bump the size of
|
|
||||||
// the slice up to the next storage class.
|
|
||||||
// This is what Grow does but we don't call Grow because
|
|
||||||
// that might copy the values twice.
|
|
||||||
s2 := append(s[:i], make(S, n+m-i)...)
|
|
||||||
copy(s2[i:], v)
|
|
||||||
copy(s2[i+m:], s[i:])
|
|
||||||
return s2
|
|
||||||
}
|
|
||||||
s = s[:n+m]
|
|
||||||
|
|
||||||
// before:
|
|
||||||
// s: aaaaaaaabbbbccccccccdddd
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
// after:
|
|
||||||
// s: aaaaaaaavvvvbbbbcccccccc
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
//
|
|
||||||
// a are the values that don't move in s.
|
|
||||||
// v are the values copied in from v.
|
|
||||||
// b and c are the values from s that are shifted up in index.
|
|
||||||
// d are the values that get overwritten, never to be seen again.
|
|
||||||
|
|
||||||
if !overlaps(v, s[i+m:]) {
|
|
||||||
// Easy case - v does not overlap either the c or d regions.
|
|
||||||
// (It might be in some of a or b, or elsewhere entirely.)
|
|
||||||
// The data we copy up doesn't write to v at all, so just do it.
|
|
||||||
|
|
||||||
copy(s[i+m:], s[i:])
|
|
||||||
|
|
||||||
// Now we have
|
|
||||||
// s: aaaaaaaabbbbbbbbcccccccc
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
// Note the b values are duplicated.
|
|
||||||
|
|
||||||
copy(s[i:], v)
|
|
||||||
|
|
||||||
// Now we have
|
|
||||||
// s: aaaaaaaavvvvbbbbcccccccc
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
// That's the result we want.
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// The hard case - v overlaps c or d. We can't just shift up
|
|
||||||
// the data because we'd move or clobber the values we're trying
|
|
||||||
// to insert.
|
|
||||||
// So instead, write v on top of d, then rotate.
|
|
||||||
copy(s[n:], v)
|
|
||||||
|
|
||||||
// Now we have
|
|
||||||
// s: aaaaaaaabbbbccccccccvvvv
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
|
|
||||||
rotateRight(s[i:], m)
|
|
||||||
|
|
||||||
// Now we have
|
|
||||||
// s: aaaaaaaavvvvbbbbcccccccc
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i i+m n n+m
|
|
||||||
// That's the result we want.
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes the elements s[i:j] from s, returning the modified slice.
|
|
||||||
// Delete panics if s[i:j] is not a valid slice of s.
|
|
||||||
// Delete is O(len(s)-j), so if many items must be deleted, it is better to
|
|
||||||
// make a single call deleting them all together than to delete one at a time.
|
|
||||||
// Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those
|
|
||||||
// elements contain pointers you might consider zeroing those elements so that
|
|
||||||
// objects they reference can be garbage collected.
|
|
||||||
func Delete[S ~[]E, E any](s S, i, j int) S {
|
|
||||||
_ = s[i:j] // bounds check
|
|
||||||
|
|
||||||
return append(s[:i], s[j:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteFunc removes any elements from s for which del returns true,
|
|
||||||
// returning the modified slice.
|
|
||||||
// When DeleteFunc removes m elements, it might not modify the elements
|
|
||||||
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
|
||||||
// zeroing those elements so that objects they reference can be garbage
|
|
||||||
// collected.
|
|
||||||
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
|
||||||
i := IndexFunc(s, del)
|
|
||||||
if i == -1 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
// Don't start copying elements until we find one to delete.
|
|
||||||
for j := i + 1; j < len(s); j++ {
|
|
||||||
if v := s[j]; !del(v) {
|
|
||||||
s[i] = v
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s[:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace replaces the elements s[i:j] by the given v, and returns the
|
|
||||||
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
|
||||||
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
|
||||||
_ = s[i:j] // verify that i:j is a valid subslice
|
|
||||||
|
|
||||||
if i == j {
|
|
||||||
return Insert(s, i, v...)
|
|
||||||
}
|
|
||||||
if j == len(s) {
|
|
||||||
return append(s[:i], v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
tot := len(s[:i]) + len(v) + len(s[j:])
|
|
||||||
if tot > cap(s) {
|
|
||||||
// Too big to fit, allocate and copy over.
|
|
||||||
s2 := append(s[:i], make(S, tot-i)...) // See Insert
|
|
||||||
copy(s2[i:], v)
|
|
||||||
copy(s2[i+len(v):], s[j:])
|
|
||||||
return s2
|
|
||||||
}
|
|
||||||
|
|
||||||
r := s[:tot]
|
|
||||||
|
|
||||||
if i+len(v) <= j {
|
|
||||||
// Easy, as v fits in the deleted portion.
|
|
||||||
copy(r[i:], v)
|
|
||||||
if i+len(v) != j {
|
|
||||||
copy(r[i+len(v):], s[j:])
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are expanding (v is bigger than j-i).
|
|
||||||
// The situation is something like this:
|
|
||||||
// (example has i=4,j=8,len(s)=16,len(v)=6)
|
|
||||||
// s: aaaaxxxxbbbbbbbbyy
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i j len(s) tot
|
|
||||||
// a: prefix of s
|
|
||||||
// x: deleted range
|
|
||||||
// b: more of s
|
|
||||||
// y: area to expand into
|
|
||||||
|
|
||||||
if !overlaps(r[i+len(v):], v) {
|
|
||||||
// Easy, as v is not clobbered by the first copy.
|
|
||||||
copy(r[i+len(v):], s[j:])
|
|
||||||
copy(r[i:], v)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a situation where we don't have a single place to which
|
|
||||||
// we can copy v. Parts of it need to go to two different places.
|
|
||||||
// We want to copy the prefix of v into y and the suffix into x, then
|
|
||||||
// rotate |y| spots to the right.
|
|
||||||
//
|
|
||||||
// v[2:] v[:2]
|
|
||||||
// | |
|
|
||||||
// s: aaaavvvvbbbbbbbbvv
|
|
||||||
// ^ ^ ^ ^
|
|
||||||
// i j len(s) tot
|
|
||||||
//
|
|
||||||
// If either of those two destinations don't alias v, then we're good.
|
|
||||||
y := len(v) - (j - i) // length of y portion
|
|
||||||
|
|
||||||
if !overlaps(r[i:j], v) {
|
|
||||||
copy(r[i:j], v[y:])
|
|
||||||
copy(r[len(s):], v[:y])
|
|
||||||
rotateRight(r[i:], y)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
if !overlaps(r[len(s):], v) {
|
|
||||||
copy(r[len(s):], v[:y])
|
|
||||||
copy(r[i:j], v[y:])
|
|
||||||
rotateRight(r[i:], y)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we know that v overlaps both x and y.
|
|
||||||
// That means that the entirety of b is *inside* v.
|
|
||||||
// So we don't need to preserve b at all; instead we
|
|
||||||
// can copy v first, then copy the b part of v out of
|
|
||||||
// v to the right destination.
|
|
||||||
k := startIdx(v, s[j:])
|
|
||||||
copy(r[i:], v)
|
|
||||||
copy(r[i+len(v):], r[i+k:])
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a copy of the slice.
|
|
||||||
// The elements are copied using assignment, so this is a shallow clone.
|
|
||||||
func Clone[S ~[]E, E any](s S) S {
|
|
||||||
// Preserve nil in case it matters.
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return append(S([]E{}), s...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compact replaces consecutive runs of equal elements with a single copy.
|
|
||||||
// This is like the uniq command found on Unix.
|
|
||||||
// Compact modifies the contents of the slice s and returns the modified slice,
|
|
||||||
// which may have a smaller length.
|
|
||||||
// When Compact discards m elements in total, it might not modify the elements
|
|
||||||
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
|
||||||
// zeroing those elements so that objects they reference can be garbage collected.
|
|
||||||
func Compact[S ~[]E, E comparable](s S) S {
|
|
||||||
if len(s) < 2 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for k := 1; k < len(s); k++ {
|
|
||||||
if s[k] != s[k-1] {
|
|
||||||
if i != k {
|
|
||||||
s[i] = s[k]
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s[:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompactFunc is like [Compact] but uses an equality function to compare elements.
|
|
||||||
// For runs of elements that compare equal, CompactFunc keeps the first one.
|
|
||||||
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
|
|
||||||
if len(s) < 2 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for k := 1; k < len(s); k++ {
|
|
||||||
if !eq(s[k], s[k-1]) {
|
|
||||||
if i != k {
|
|
||||||
s[i] = s[k]
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s[:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grow increases the slice's capacity, if necessary, to guarantee space for
|
|
||||||
// another n elements. After Grow(n), at least n elements can be appended
|
|
||||||
// to the slice without another allocation. If n is negative or too large to
|
|
||||||
// allocate the memory, Grow panics.
|
|
||||||
func Grow[S ~[]E, E any](s S, n int) S {
|
|
||||||
if n < 0 {
|
|
||||||
panic("cannot be negative")
|
|
||||||
}
|
|
||||||
if n -= cap(s) - len(s); n > 0 {
|
|
||||||
// TODO(https://go.dev/issue/53888): Make using []E instead of S
|
|
||||||
// to workaround a compiler bug where the runtime.growslice optimization
|
|
||||||
// does not take effect. Revert when the compiler is fixed.
|
|
||||||
s = append([]E(s)[:cap(s)], make([]E, n)...)[:len(s)]
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
|
|
||||||
func Clip[S ~[]E, E any](s S) S {
|
|
||||||
return s[:len(s):len(s)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotation algorithm explanation:
|
|
||||||
//
|
|
||||||
// rotate left by 2
|
|
||||||
// start with
|
|
||||||
// 0123456789
|
|
||||||
// split up like this
|
|
||||||
// 01 234567 89
|
|
||||||
// swap first 2 and last 2
|
|
||||||
// 89 234567 01
|
|
||||||
// join first parts
|
|
||||||
// 89234567 01
|
|
||||||
// recursively rotate first left part by 2
|
|
||||||
// 23456789 01
|
|
||||||
// join at the end
|
|
||||||
// 2345678901
|
|
||||||
//
|
|
||||||
// rotate left by 8
|
|
||||||
// start with
|
|
||||||
// 0123456789
|
|
||||||
// split up like this
|
|
||||||
// 01 234567 89
|
|
||||||
// swap first 2 and last 2
|
|
||||||
// 89 234567 01
|
|
||||||
// join last parts
|
|
||||||
// 89 23456701
|
|
||||||
// recursively rotate second part left by 6
|
|
||||||
// 89 01234567
|
|
||||||
// join at the end
|
|
||||||
// 8901234567
|
|
||||||
|
|
||||||
// TODO: There are other rotate algorithms.
|
|
||||||
// This algorithm has the desirable property that it moves each element exactly twice.
|
|
||||||
// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes.
|
|
||||||
// The follow-cycles algorithm can be 1-write but it is not very cache friendly.
|
|
||||||
|
|
||||||
// rotateLeft rotates b left by n spaces.
|
|
||||||
// s_final[i] = s_orig[i+r], wrapping around.
|
|
||||||
func rotateLeft[E any](s []E, r int) {
|
|
||||||
for r != 0 && r != len(s) {
|
|
||||||
if r*2 <= len(s) {
|
|
||||||
swap(s[:r], s[len(s)-r:])
|
|
||||||
s = s[:len(s)-r]
|
|
||||||
} else {
|
|
||||||
swap(s[:len(s)-r], s[r:])
|
|
||||||
s, r = s[len(s)-r:], r*2-len(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func rotateRight[E any](s []E, r int) {
|
|
||||||
rotateLeft(s, len(s)-r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swap swaps the contents of x and y. x and y must be equal length and disjoint.
|
|
||||||
func swap[E any](x, y []E) {
|
|
||||||
for i := 0; i < len(x); i++ {
|
|
||||||
x[i], y[i] = y[i], x[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap.
|
|
||||||
func overlaps[E any](a, b []E) bool {
|
|
||||||
if len(a) == 0 || len(b) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
elemSize := unsafe.Sizeof(a[0])
|
|
||||||
if elemSize == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// TODO: use a runtime/unsafe facility once one becomes available. See issue 12445.
|
|
||||||
// Also see crypto/internal/alias/alias.go:AnyOverlap
|
|
||||||
return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) &&
|
|
||||||
uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// startIdx returns the index in haystack where the needle starts.
|
|
||||||
// prerequisite: the needle must be aliased entirely inside the haystack.
|
|
||||||
func startIdx[E any](haystack, needle []E) int {
|
|
||||||
p := &needle[0]
|
|
||||||
for i := range haystack {
|
|
||||||
if p == &haystack[i] {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: what if the overlap is by a non-integral number of Es?
|
|
||||||
panic("needle not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse reverses the elements of the slice in place.
|
|
||||||
func Reverse[S ~[]E, E any](s S) {
|
|
||||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
}
|
|
195
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
195
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
|
@ -1,195 +0,0 @@
|
||||||
// Copyright 2022 The Go 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:generate go run $GOROOT/src/sort/gen_sort_variants.go -exp
|
|
||||||
|
|
||||||
package slices
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/bits"
|
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sort sorts a slice of any ordered type in ascending order.
|
|
||||||
// When sorting floating-point numbers, NaNs are ordered before other values.
|
|
||||||
func Sort[S ~[]E, E constraints.Ordered](x S) {
|
|
||||||
n := len(x)
|
|
||||||
pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortFunc sorts the slice x in ascending order as determined by the cmp
|
|
||||||
// function. This sort is not guaranteed to be stable.
|
|
||||||
// cmp(a, b) should return a negative number when a < b, a positive number when
|
|
||||||
// a > b and zero when a == b.
|
|
||||||
//
|
|
||||||
// SortFunc requires that cmp is a strict weak ordering.
|
|
||||||
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
|
|
||||||
func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
|
||||||
n := len(x)
|
|
||||||
pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortStableFunc sorts the slice x while keeping the original order of equal
|
|
||||||
// elements, using cmp to compare elements in the same way as [SortFunc].
|
|
||||||
func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
|
||||||
stableCmpFunc(x, len(x), cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSorted reports whether x is sorted in ascending order.
|
|
||||||
func IsSorted[S ~[]E, E constraints.Ordered](x S) bool {
|
|
||||||
for i := len(x) - 1; i > 0; i-- {
|
|
||||||
if cmpLess(x[i], x[i-1]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the
|
|
||||||
// comparison function as defined by [SortFunc].
|
|
||||||
func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool {
|
|
||||||
for i := len(x) - 1; i > 0; i-- {
|
|
||||||
if cmp(x[i], x[i-1]) < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min returns the minimal value in x. It panics if x is empty.
|
|
||||||
// For floating-point numbers, Min propagates NaNs (any NaN value in x
|
|
||||||
// forces the output to be NaN).
|
|
||||||
func Min[S ~[]E, E constraints.Ordered](x S) E {
|
|
||||||
if len(x) < 1 {
|
|
||||||
panic("slices.Min: empty list")
|
|
||||||
}
|
|
||||||
m := x[0]
|
|
||||||
for i := 1; i < len(x); i++ {
|
|
||||||
m = min(m, x[i])
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinFunc returns the minimal value in x, using cmp to compare elements.
|
|
||||||
// It panics if x is empty. If there is more than one minimal element
|
|
||||||
// according to the cmp function, MinFunc returns the first one.
|
|
||||||
func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
|
||||||
if len(x) < 1 {
|
|
||||||
panic("slices.MinFunc: empty list")
|
|
||||||
}
|
|
||||||
m := x[0]
|
|
||||||
for i := 1; i < len(x); i++ {
|
|
||||||
if cmp(x[i], m) < 0 {
|
|
||||||
m = x[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max returns the maximal value in x. It panics if x is empty.
|
|
||||||
// For floating-point E, Max propagates NaNs (any NaN value in x
|
|
||||||
// forces the output to be NaN).
|
|
||||||
func Max[S ~[]E, E constraints.Ordered](x S) E {
|
|
||||||
if len(x) < 1 {
|
|
||||||
panic("slices.Max: empty list")
|
|
||||||
}
|
|
||||||
m := x[0]
|
|
||||||
for i := 1; i < len(x); i++ {
|
|
||||||
m = max(m, x[i])
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxFunc returns the maximal value in x, using cmp to compare elements.
|
|
||||||
// It panics if x is empty. If there is more than one maximal element
|
|
||||||
// according to the cmp function, MaxFunc returns the first one.
|
|
||||||
func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
|
||||||
if len(x) < 1 {
|
|
||||||
panic("slices.MaxFunc: empty list")
|
|
||||||
}
|
|
||||||
m := x[0]
|
|
||||||
for i := 1; i < len(x); i++ {
|
|
||||||
if cmp(x[i], m) > 0 {
|
|
||||||
m = x[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinarySearch searches for target in a sorted slice and returns the position
|
|
||||||
// where target is found, or the position where target would appear in the
|
|
||||||
// sort order; it also returns a bool saying whether the target is really found
|
|
||||||
// in the slice. The slice must be sorted in increasing order.
|
|
||||||
func BinarySearch[S ~[]E, E constraints.Ordered](x S, target E) (int, bool) {
|
|
||||||
// Inlining is faster than calling BinarySearchFunc with a lambda.
|
|
||||||
n := len(x)
|
|
||||||
// Define x[-1] < target and x[n] >= target.
|
|
||||||
// Invariant: x[i-1] < target, x[j] >= target.
|
|
||||||
i, j := 0, n
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
|
||||||
// i ≤ h < j
|
|
||||||
if cmpLess(x[h], target) {
|
|
||||||
i = h + 1 // preserves x[i-1] < target
|
|
||||||
} else {
|
|
||||||
j = h // preserves x[j] >= target
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i.
|
|
||||||
return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinarySearchFunc works like [BinarySearch], but uses a custom comparison
|
|
||||||
// function. The slice must be sorted in increasing order, where "increasing"
|
|
||||||
// is defined by cmp. cmp should return 0 if the slice element matches
|
|
||||||
// the target, a negative number if the slice element precedes the target,
|
|
||||||
// or a positive number if the slice element follows the target.
|
|
||||||
// cmp must implement the same ordering as the slice, such that if
|
|
||||||
// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
|
|
||||||
func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) {
|
|
||||||
n := len(x)
|
|
||||||
// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
|
|
||||||
// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
|
|
||||||
i, j := 0, n
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
|
||||||
// i ≤ h < j
|
|
||||||
if cmp(x[h], target) < 0 {
|
|
||||||
i = h + 1 // preserves cmp(x[i - 1], target) < 0
|
|
||||||
} else {
|
|
||||||
j = h // preserves cmp(x[j], target) >= 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i.
|
|
||||||
return i, i < n && cmp(x[i], target) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortedHint int // hint for pdqsort when choosing the pivot
|
|
||||||
|
|
||||||
const (
|
|
||||||
unknownHint sortedHint = iota
|
|
||||||
increasingHint
|
|
||||||
decreasingHint
|
|
||||||
)
|
|
||||||
|
|
||||||
// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
|
|
||||||
type xorshift uint64
|
|
||||||
|
|
||||||
func (r *xorshift) Next() uint64 {
|
|
||||||
*r ^= *r << 13
|
|
||||||
*r ^= *r >> 17
|
|
||||||
*r ^= *r << 5
|
|
||||||
return uint64(*r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextPowerOfTwo(length int) uint {
|
|
||||||
return 1 << bits.Len(uint(length))
|
|
||||||
}
|
|
||||||
|
|
||||||
// isNaN reports whether x is a NaN without requiring the math package.
|
|
||||||
// This will always return false if T is not floating-point.
|
|
||||||
func isNaN[T constraints.Ordered](x T) bool {
|
|
||||||
return x != x
|
|
||||||
}
|
|
479
vendor/golang.org/x/exp/slices/zsortanyfunc.go
generated
vendored
479
vendor/golang.org/x/exp/slices/zsortanyfunc.go
generated
vendored
|
@ -1,479 +0,0 @@
|
||||||
// Code generated by gen_sort_variants.go; DO NOT EDIT.
|
|
||||||
|
|
||||||
// Copyright 2022 The Go 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 slices
|
|
||||||
|
|
||||||
// insertionSortCmpFunc sorts data[a:b] using insertion sort.
|
|
||||||
func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
|
||||||
for i := a + 1; i < b; i++ {
|
|
||||||
for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- {
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// siftDownCmpFunc implements the heap property on data[lo:hi].
|
|
||||||
// first is an offset into the array where the root of the heap lies.
|
|
||||||
func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) {
|
|
||||||
root := lo
|
|
||||||
for {
|
|
||||||
child := 2*root + 1
|
|
||||||
if child >= hi {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) {
|
|
||||||
child++
|
|
||||||
}
|
|
||||||
if !(cmp(data[first+root], data[first+child]) < 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
|
||||||
root = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
|
||||||
first := a
|
|
||||||
lo := 0
|
|
||||||
hi := b - a
|
|
||||||
|
|
||||||
// Build heap with greatest element at top.
|
|
||||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
|
||||||
siftDownCmpFunc(data, i, hi, first, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop elements, largest first, into end of data.
|
|
||||||
for i := hi - 1; i >= 0; i-- {
|
|
||||||
data[first], data[first+i] = data[first+i], data[first]
|
|
||||||
siftDownCmpFunc(data, lo, i, first, cmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdqsortCmpFunc sorts data[a:b].
|
|
||||||
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
|
||||||
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
|
||||||
// C++ implementation: https://github.com/orlp/pdqsort
|
|
||||||
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
|
||||||
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
|
||||||
func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) {
|
|
||||||
const maxInsertion = 12
|
|
||||||
|
|
||||||
var (
|
|
||||||
wasBalanced = true // whether the last partitioning was reasonably balanced
|
|
||||||
wasPartitioned = true // whether the slice was already partitioned
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
length := b - a
|
|
||||||
|
|
||||||
if length <= maxInsertion {
|
|
||||||
insertionSortCmpFunc(data, a, b, cmp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to heapsort if too many bad choices were made.
|
|
||||||
if limit == 0 {
|
|
||||||
heapSortCmpFunc(data, a, b, cmp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the last partitioning was imbalanced, we need to breaking patterns.
|
|
||||||
if !wasBalanced {
|
|
||||||
breakPatternsCmpFunc(data, a, b, cmp)
|
|
||||||
limit--
|
|
||||||
}
|
|
||||||
|
|
||||||
pivot, hint := choosePivotCmpFunc(data, a, b, cmp)
|
|
||||||
if hint == decreasingHint {
|
|
||||||
reverseRangeCmpFunc(data, a, b, cmp)
|
|
||||||
// The chosen pivot was pivot-a elements after the start of the array.
|
|
||||||
// After reversing it is pivot-a elements before the end of the array.
|
|
||||||
// The idea came from Rust's implementation.
|
|
||||||
pivot = (b - 1) - (pivot - a)
|
|
||||||
hint = increasingHint
|
|
||||||
}
|
|
||||||
|
|
||||||
// The slice is likely already sorted.
|
|
||||||
if wasBalanced && wasPartitioned && hint == increasingHint {
|
|
||||||
if partialInsertionSortCmpFunc(data, a, b, cmp) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probably the slice contains many duplicate elements, partition the slice into
|
|
||||||
// elements equal to and elements greater than the pivot.
|
|
||||||
if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) {
|
|
||||||
mid := partitionEqualCmpFunc(data, a, b, pivot, cmp)
|
|
||||||
a = mid
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp)
|
|
||||||
wasPartitioned = alreadyPartitioned
|
|
||||||
|
|
||||||
leftLen, rightLen := mid-a, b-mid
|
|
||||||
balanceThreshold := length / 8
|
|
||||||
if leftLen < rightLen {
|
|
||||||
wasBalanced = leftLen >= balanceThreshold
|
|
||||||
pdqsortCmpFunc(data, a, mid, limit, cmp)
|
|
||||||
a = mid + 1
|
|
||||||
} else {
|
|
||||||
wasBalanced = rightLen >= balanceThreshold
|
|
||||||
pdqsortCmpFunc(data, mid+1, b, limit, cmp)
|
|
||||||
b = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// partitionCmpFunc does one quicksort partition.
|
|
||||||
// Let p = data[pivot]
|
|
||||||
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
|
||||||
// On return, data[newpivot] = p
|
|
||||||
func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) {
|
|
||||||
data[a], data[pivot] = data[pivot], data[a]
|
|
||||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
|
||||||
|
|
||||||
for i <= j && (cmp(data[i], data[a]) < 0) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && !(cmp(data[j], data[a]) < 0) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
data[j], data[a] = data[a], data[j]
|
|
||||||
return j, true
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
|
|
||||||
for {
|
|
||||||
for i <= j && (cmp(data[i], data[a]) < 0) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && !(cmp(data[j], data[a]) < 0) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
data[j], data[a] = data[a], data[j]
|
|
||||||
return j, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
|
||||||
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
|
||||||
func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) {
|
|
||||||
data[a], data[pivot] = data[pivot], data[a]
|
|
||||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
|
||||||
|
|
||||||
for {
|
|
||||||
for i <= j && !(cmp(data[a], data[i]) < 0) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && (cmp(data[a], data[j]) < 0) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end.
|
|
||||||
func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool {
|
|
||||||
const (
|
|
||||||
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
|
||||||
shortestShifting = 50 // don't shift any elements on short arrays
|
|
||||||
)
|
|
||||||
i := a + 1
|
|
||||||
for j := 0; j < maxSteps; j++ {
|
|
||||||
for i < b && !(cmp(data[i], data[i-1]) < 0) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == b {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if b-a < shortestShifting {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
data[i], data[i-1] = data[i-1], data[i]
|
|
||||||
|
|
||||||
// Shift the smaller one to the left.
|
|
||||||
if i-a >= 2 {
|
|
||||||
for j := i - 1; j >= 1; j-- {
|
|
||||||
if !(cmp(data[j], data[j-1]) < 0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Shift the greater one to the right.
|
|
||||||
if b-i >= 2 {
|
|
||||||
for j := i + 1; j < b; j++ {
|
|
||||||
if !(cmp(data[j], data[j-1]) < 0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns
|
|
||||||
// that might cause imbalanced partitions in quicksort.
|
|
||||||
func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
|
||||||
length := b - a
|
|
||||||
if length >= 8 {
|
|
||||||
random := xorshift(length)
|
|
||||||
modulus := nextPowerOfTwo(length)
|
|
||||||
|
|
||||||
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
|
||||||
other := int(uint(random.Next()) & (modulus - 1))
|
|
||||||
if other >= length {
|
|
||||||
other -= length
|
|
||||||
}
|
|
||||||
data[idx], data[a+other] = data[a+other], data[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// choosePivotCmpFunc chooses a pivot in data[a:b].
|
|
||||||
//
|
|
||||||
// [0,8): chooses a static pivot.
|
|
||||||
// [8,shortestNinther): uses the simple median-of-three method.
|
|
||||||
// [shortestNinther,∞): uses the Tukey ninther method.
|
|
||||||
func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) {
|
|
||||||
const (
|
|
||||||
shortestNinther = 50
|
|
||||||
maxSwaps = 4 * 3
|
|
||||||
)
|
|
||||||
|
|
||||||
l := b - a
|
|
||||||
|
|
||||||
var (
|
|
||||||
swaps int
|
|
||||||
i = a + l/4*1
|
|
||||||
j = a + l/4*2
|
|
||||||
k = a + l/4*3
|
|
||||||
)
|
|
||||||
|
|
||||||
if l >= 8 {
|
|
||||||
if l >= shortestNinther {
|
|
||||||
// Tukey ninther method, the idea came from Rust's implementation.
|
|
||||||
i = medianAdjacentCmpFunc(data, i, &swaps, cmp)
|
|
||||||
j = medianAdjacentCmpFunc(data, j, &swaps, cmp)
|
|
||||||
k = medianAdjacentCmpFunc(data, k, &swaps, cmp)
|
|
||||||
}
|
|
||||||
// Find the median among i, j, k and stores it into j.
|
|
||||||
j = medianCmpFunc(data, i, j, k, &swaps, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch swaps {
|
|
||||||
case 0:
|
|
||||||
return j, increasingHint
|
|
||||||
case maxSwaps:
|
|
||||||
return j, decreasingHint
|
|
||||||
default:
|
|
||||||
return j, unknownHint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
|
||||||
func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) {
|
|
||||||
if cmp(data[b], data[a]) < 0 {
|
|
||||||
*swaps++
|
|
||||||
return b, a
|
|
||||||
}
|
|
||||||
return a, b
|
|
||||||
}
|
|
||||||
|
|
||||||
// medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
|
||||||
func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int {
|
|
||||||
a, b = order2CmpFunc(data, a, b, swaps, cmp)
|
|
||||||
b, c = order2CmpFunc(data, b, c, swaps, cmp)
|
|
||||||
a, b = order2CmpFunc(data, a, b, swaps, cmp)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
|
||||||
func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int {
|
|
||||||
return medianCmpFunc(data, a-1, a, a+1, swaps, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
|
||||||
i := a
|
|
||||||
j := b - 1
|
|
||||||
for i < j {
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
data[a+i], data[b+i] = data[b+i], data[a+i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) {
|
|
||||||
blockSize := 20 // must be > 0
|
|
||||||
a, b := 0, blockSize
|
|
||||||
for b <= n {
|
|
||||||
insertionSortCmpFunc(data, a, b, cmp)
|
|
||||||
a = b
|
|
||||||
b += blockSize
|
|
||||||
}
|
|
||||||
insertionSortCmpFunc(data, a, n, cmp)
|
|
||||||
|
|
||||||
for blockSize < n {
|
|
||||||
a, b = 0, 2*blockSize
|
|
||||||
for b <= n {
|
|
||||||
symMergeCmpFunc(data, a, a+blockSize, b, cmp)
|
|
||||||
a = b
|
|
||||||
b += 2 * blockSize
|
|
||||||
}
|
|
||||||
if m := a + blockSize; m < n {
|
|
||||||
symMergeCmpFunc(data, a, m, n, cmp)
|
|
||||||
}
|
|
||||||
blockSize *= 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using
|
|
||||||
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
|
|
||||||
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
|
|
||||||
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
|
|
||||||
// Computer Science, pages 714-723. Springer, 2004.
|
|
||||||
//
|
|
||||||
// Let M = m-a and N = b-n. Wolog M < N.
|
|
||||||
// The recursion depth is bound by ceil(log(N+M)).
|
|
||||||
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
|
|
||||||
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
|
|
||||||
//
|
|
||||||
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
|
|
||||||
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
|
|
||||||
// in the paper carries through for Swap operations, especially as the block
|
|
||||||
// swapping rotate uses only O(M+N) Swaps.
|
|
||||||
//
|
|
||||||
// symMerge assumes non-degenerate arguments: a < m && m < b.
|
|
||||||
// Having the caller check this condition eliminates many leaf recursion calls,
|
|
||||||
// which improves performance.
|
|
||||||
func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
|
|
||||||
// Avoid unnecessary recursions of symMerge
|
|
||||||
// by direct insertion of data[a] into data[m:b]
|
|
||||||
// if data[a:m] only contains one element.
|
|
||||||
if m-a == 1 {
|
|
||||||
// Use binary search to find the lowest index i
|
|
||||||
// such that data[i] >= data[a] for m <= i < b.
|
|
||||||
// Exit the search loop with i == b in case no such index exists.
|
|
||||||
i := m
|
|
||||||
j := b
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1)
|
|
||||||
if cmp(data[h], data[a]) < 0 {
|
|
||||||
i = h + 1
|
|
||||||
} else {
|
|
||||||
j = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Swap values until data[a] reaches the position before i.
|
|
||||||
for k := a; k < i-1; k++ {
|
|
||||||
data[k], data[k+1] = data[k+1], data[k]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid unnecessary recursions of symMerge
|
|
||||||
// by direct insertion of data[m] into data[a:m]
|
|
||||||
// if data[m:b] only contains one element.
|
|
||||||
if b-m == 1 {
|
|
||||||
// Use binary search to find the lowest index i
|
|
||||||
// such that data[i] > data[m] for a <= i < m.
|
|
||||||
// Exit the search loop with i == m in case no such index exists.
|
|
||||||
i := a
|
|
||||||
j := m
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1)
|
|
||||||
if !(cmp(data[m], data[h]) < 0) {
|
|
||||||
i = h + 1
|
|
||||||
} else {
|
|
||||||
j = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Swap values until data[m] reaches the position i.
|
|
||||||
for k := m; k > i; k-- {
|
|
||||||
data[k], data[k-1] = data[k-1], data[k]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mid := int(uint(a+b) >> 1)
|
|
||||||
n := mid + m
|
|
||||||
var start, r int
|
|
||||||
if m > mid {
|
|
||||||
start = n - b
|
|
||||||
r = mid
|
|
||||||
} else {
|
|
||||||
start = a
|
|
||||||
r = m
|
|
||||||
}
|
|
||||||
p := n - 1
|
|
||||||
|
|
||||||
for start < r {
|
|
||||||
c := int(uint(start+r) >> 1)
|
|
||||||
if !(cmp(data[p-c], data[c]) < 0) {
|
|
||||||
start = c + 1
|
|
||||||
} else {
|
|
||||||
r = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end := n - start
|
|
||||||
if start < m && m < end {
|
|
||||||
rotateCmpFunc(data, start, m, end, cmp)
|
|
||||||
}
|
|
||||||
if a < start && start < mid {
|
|
||||||
symMergeCmpFunc(data, a, start, mid, cmp)
|
|
||||||
}
|
|
||||||
if mid < end && end < b {
|
|
||||||
symMergeCmpFunc(data, mid, end, b, cmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
|
|
||||||
// Data of the form 'x u v y' is changed to 'x v u y'.
|
|
||||||
// rotate performs at most b-a many calls to data.Swap,
|
|
||||||
// and it assumes non-degenerate arguments: a < m && m < b.
|
|
||||||
func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
|
|
||||||
i := m - a
|
|
||||||
j := b - m
|
|
||||||
|
|
||||||
for i != j {
|
|
||||||
if i > j {
|
|
||||||
swapRangeCmpFunc(data, m-i, m, j, cmp)
|
|
||||||
i -= j
|
|
||||||
} else {
|
|
||||||
swapRangeCmpFunc(data, m-i, m+j-i, i, cmp)
|
|
||||||
j -= i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// i == j
|
|
||||||
swapRangeCmpFunc(data, m-i, m, i, cmp)
|
|
||||||
}
|
|
481
vendor/golang.org/x/exp/slices/zsortordered.go
generated
vendored
481
vendor/golang.org/x/exp/slices/zsortordered.go
generated
vendored
|
@ -1,481 +0,0 @@
|
||||||
// Code generated by gen_sort_variants.go; DO NOT EDIT.
|
|
||||||
|
|
||||||
// Copyright 2022 The Go 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 slices
|
|
||||||
|
|
||||||
import "golang.org/x/exp/constraints"
|
|
||||||
|
|
||||||
// insertionSortOrdered sorts data[a:b] using insertion sort.
|
|
||||||
func insertionSortOrdered[E constraints.Ordered](data []E, a, b int) {
|
|
||||||
for i := a + 1; i < b; i++ {
|
|
||||||
for j := i; j > a && cmpLess(data[j], data[j-1]); j-- {
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// siftDownOrdered implements the heap property on data[lo:hi].
|
|
||||||
// first is an offset into the array where the root of the heap lies.
|
|
||||||
func siftDownOrdered[E constraints.Ordered](data []E, lo, hi, first int) {
|
|
||||||
root := lo
|
|
||||||
for {
|
|
||||||
child := 2*root + 1
|
|
||||||
if child >= hi {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if child+1 < hi && cmpLess(data[first+child], data[first+child+1]) {
|
|
||||||
child++
|
|
||||||
}
|
|
||||||
if !cmpLess(data[first+root], data[first+child]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
|
||||||
root = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func heapSortOrdered[E constraints.Ordered](data []E, a, b int) {
|
|
||||||
first := a
|
|
||||||
lo := 0
|
|
||||||
hi := b - a
|
|
||||||
|
|
||||||
// Build heap with greatest element at top.
|
|
||||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
|
||||||
siftDownOrdered(data, i, hi, first)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop elements, largest first, into end of data.
|
|
||||||
for i := hi - 1; i >= 0; i-- {
|
|
||||||
data[first], data[first+i] = data[first+i], data[first]
|
|
||||||
siftDownOrdered(data, lo, i, first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pdqsortOrdered sorts data[a:b].
|
|
||||||
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
|
||||||
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
|
||||||
// C++ implementation: https://github.com/orlp/pdqsort
|
|
||||||
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
|
||||||
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
|
||||||
func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) {
|
|
||||||
const maxInsertion = 12
|
|
||||||
|
|
||||||
var (
|
|
||||||
wasBalanced = true // whether the last partitioning was reasonably balanced
|
|
||||||
wasPartitioned = true // whether the slice was already partitioned
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
length := b - a
|
|
||||||
|
|
||||||
if length <= maxInsertion {
|
|
||||||
insertionSortOrdered(data, a, b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to heapsort if too many bad choices were made.
|
|
||||||
if limit == 0 {
|
|
||||||
heapSortOrdered(data, a, b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the last partitioning was imbalanced, we need to breaking patterns.
|
|
||||||
if !wasBalanced {
|
|
||||||
breakPatternsOrdered(data, a, b)
|
|
||||||
limit--
|
|
||||||
}
|
|
||||||
|
|
||||||
pivot, hint := choosePivotOrdered(data, a, b)
|
|
||||||
if hint == decreasingHint {
|
|
||||||
reverseRangeOrdered(data, a, b)
|
|
||||||
// The chosen pivot was pivot-a elements after the start of the array.
|
|
||||||
// After reversing it is pivot-a elements before the end of the array.
|
|
||||||
// The idea came from Rust's implementation.
|
|
||||||
pivot = (b - 1) - (pivot - a)
|
|
||||||
hint = increasingHint
|
|
||||||
}
|
|
||||||
|
|
||||||
// The slice is likely already sorted.
|
|
||||||
if wasBalanced && wasPartitioned && hint == increasingHint {
|
|
||||||
if partialInsertionSortOrdered(data, a, b) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probably the slice contains many duplicate elements, partition the slice into
|
|
||||||
// elements equal to and elements greater than the pivot.
|
|
||||||
if a > 0 && !cmpLess(data[a-1], data[pivot]) {
|
|
||||||
mid := partitionEqualOrdered(data, a, b, pivot)
|
|
||||||
a = mid
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot)
|
|
||||||
wasPartitioned = alreadyPartitioned
|
|
||||||
|
|
||||||
leftLen, rightLen := mid-a, b-mid
|
|
||||||
balanceThreshold := length / 8
|
|
||||||
if leftLen < rightLen {
|
|
||||||
wasBalanced = leftLen >= balanceThreshold
|
|
||||||
pdqsortOrdered(data, a, mid, limit)
|
|
||||||
a = mid + 1
|
|
||||||
} else {
|
|
||||||
wasBalanced = rightLen >= balanceThreshold
|
|
||||||
pdqsortOrdered(data, mid+1, b, limit)
|
|
||||||
b = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// partitionOrdered does one quicksort partition.
|
|
||||||
// Let p = data[pivot]
|
|
||||||
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
|
||||||
// On return, data[newpivot] = p
|
|
||||||
func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
|
|
||||||
data[a], data[pivot] = data[pivot], data[a]
|
|
||||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
|
||||||
|
|
||||||
for i <= j && cmpLess(data[i], data[a]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && !cmpLess(data[j], data[a]) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
data[j], data[a] = data[a], data[j]
|
|
||||||
return j, true
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
|
|
||||||
for {
|
|
||||||
for i <= j && cmpLess(data[i], data[a]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && !cmpLess(data[j], data[a]) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
data[j], data[a] = data[a], data[j]
|
|
||||||
return j, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
|
||||||
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
|
||||||
func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int) {
|
|
||||||
data[a], data[pivot] = data[pivot], data[a]
|
|
||||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
|
||||||
|
|
||||||
for {
|
|
||||||
for i <= j && !cmpLess(data[a], data[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
for i <= j && cmpLess(data[a], data[j]) {
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
if i > j {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end.
|
|
||||||
func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool {
|
|
||||||
const (
|
|
||||||
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
|
||||||
shortestShifting = 50 // don't shift any elements on short arrays
|
|
||||||
)
|
|
||||||
i := a + 1
|
|
||||||
for j := 0; j < maxSteps; j++ {
|
|
||||||
for i < b && !cmpLess(data[i], data[i-1]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == b {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if b-a < shortestShifting {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
data[i], data[i-1] = data[i-1], data[i]
|
|
||||||
|
|
||||||
// Shift the smaller one to the left.
|
|
||||||
if i-a >= 2 {
|
|
||||||
for j := i - 1; j >= 1; j-- {
|
|
||||||
if !cmpLess(data[j], data[j-1]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Shift the greater one to the right.
|
|
||||||
if b-i >= 2 {
|
|
||||||
for j := i + 1; j < b; j++ {
|
|
||||||
if !cmpLess(data[j], data[j-1]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data[j], data[j-1] = data[j-1], data[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// breakPatternsOrdered scatters some elements around in an attempt to break some patterns
|
|
||||||
// that might cause imbalanced partitions in quicksort.
|
|
||||||
func breakPatternsOrdered[E constraints.Ordered](data []E, a, b int) {
|
|
||||||
length := b - a
|
|
||||||
if length >= 8 {
|
|
||||||
random := xorshift(length)
|
|
||||||
modulus := nextPowerOfTwo(length)
|
|
||||||
|
|
||||||
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
|
||||||
other := int(uint(random.Next()) & (modulus - 1))
|
|
||||||
if other >= length {
|
|
||||||
other -= length
|
|
||||||
}
|
|
||||||
data[idx], data[a+other] = data[a+other], data[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// choosePivotOrdered chooses a pivot in data[a:b].
|
|
||||||
//
|
|
||||||
// [0,8): chooses a static pivot.
|
|
||||||
// [8,shortestNinther): uses the simple median-of-three method.
|
|
||||||
// [shortestNinther,∞): uses the Tukey ninther method.
|
|
||||||
func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, hint sortedHint) {
|
|
||||||
const (
|
|
||||||
shortestNinther = 50
|
|
||||||
maxSwaps = 4 * 3
|
|
||||||
)
|
|
||||||
|
|
||||||
l := b - a
|
|
||||||
|
|
||||||
var (
|
|
||||||
swaps int
|
|
||||||
i = a + l/4*1
|
|
||||||
j = a + l/4*2
|
|
||||||
k = a + l/4*3
|
|
||||||
)
|
|
||||||
|
|
||||||
if l >= 8 {
|
|
||||||
if l >= shortestNinther {
|
|
||||||
// Tukey ninther method, the idea came from Rust's implementation.
|
|
||||||
i = medianAdjacentOrdered(data, i, &swaps)
|
|
||||||
j = medianAdjacentOrdered(data, j, &swaps)
|
|
||||||
k = medianAdjacentOrdered(data, k, &swaps)
|
|
||||||
}
|
|
||||||
// Find the median among i, j, k and stores it into j.
|
|
||||||
j = medianOrdered(data, i, j, k, &swaps)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch swaps {
|
|
||||||
case 0:
|
|
||||||
return j, increasingHint
|
|
||||||
case maxSwaps:
|
|
||||||
return j, decreasingHint
|
|
||||||
default:
|
|
||||||
return j, unknownHint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
|
||||||
func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) {
|
|
||||||
if cmpLess(data[b], data[a]) {
|
|
||||||
*swaps++
|
|
||||||
return b, a
|
|
||||||
}
|
|
||||||
return a, b
|
|
||||||
}
|
|
||||||
|
|
||||||
// medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
|
||||||
func medianOrdered[E constraints.Ordered](data []E, a, b, c int, swaps *int) int {
|
|
||||||
a, b = order2Ordered(data, a, b, swaps)
|
|
||||||
b, c = order2Ordered(data, b, c, swaps)
|
|
||||||
a, b = order2Ordered(data, a, b, swaps)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
|
||||||
func medianAdjacentOrdered[E constraints.Ordered](data []E, a int, swaps *int) int {
|
|
||||||
return medianOrdered(data, a-1, a, a+1, swaps)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverseRangeOrdered[E constraints.Ordered](data []E, a, b int) {
|
|
||||||
i := a
|
|
||||||
j := b - 1
|
|
||||||
for i < j {
|
|
||||||
data[i], data[j] = data[j], data[i]
|
|
||||||
i++
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func swapRangeOrdered[E constraints.Ordered](data []E, a, b, n int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
data[a+i], data[b+i] = data[b+i], data[a+i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stableOrdered[E constraints.Ordered](data []E, n int) {
|
|
||||||
blockSize := 20 // must be > 0
|
|
||||||
a, b := 0, blockSize
|
|
||||||
for b <= n {
|
|
||||||
insertionSortOrdered(data, a, b)
|
|
||||||
a = b
|
|
||||||
b += blockSize
|
|
||||||
}
|
|
||||||
insertionSortOrdered(data, a, n)
|
|
||||||
|
|
||||||
for blockSize < n {
|
|
||||||
a, b = 0, 2*blockSize
|
|
||||||
for b <= n {
|
|
||||||
symMergeOrdered(data, a, a+blockSize, b)
|
|
||||||
a = b
|
|
||||||
b += 2 * blockSize
|
|
||||||
}
|
|
||||||
if m := a + blockSize; m < n {
|
|
||||||
symMergeOrdered(data, a, m, n)
|
|
||||||
}
|
|
||||||
blockSize *= 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using
|
|
||||||
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
|
|
||||||
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
|
|
||||||
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
|
|
||||||
// Computer Science, pages 714-723. Springer, 2004.
|
|
||||||
//
|
|
||||||
// Let M = m-a and N = b-n. Wolog M < N.
|
|
||||||
// The recursion depth is bound by ceil(log(N+M)).
|
|
||||||
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
|
|
||||||
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
|
|
||||||
//
|
|
||||||
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
|
|
||||||
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
|
|
||||||
// in the paper carries through for Swap operations, especially as the block
|
|
||||||
// swapping rotate uses only O(M+N) Swaps.
|
|
||||||
//
|
|
||||||
// symMerge assumes non-degenerate arguments: a < m && m < b.
|
|
||||||
// Having the caller check this condition eliminates many leaf recursion calls,
|
|
||||||
// which improves performance.
|
|
||||||
func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) {
|
|
||||||
// Avoid unnecessary recursions of symMerge
|
|
||||||
// by direct insertion of data[a] into data[m:b]
|
|
||||||
// if data[a:m] only contains one element.
|
|
||||||
if m-a == 1 {
|
|
||||||
// Use binary search to find the lowest index i
|
|
||||||
// such that data[i] >= data[a] for m <= i < b.
|
|
||||||
// Exit the search loop with i == b in case no such index exists.
|
|
||||||
i := m
|
|
||||||
j := b
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1)
|
|
||||||
if cmpLess(data[h], data[a]) {
|
|
||||||
i = h + 1
|
|
||||||
} else {
|
|
||||||
j = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Swap values until data[a] reaches the position before i.
|
|
||||||
for k := a; k < i-1; k++ {
|
|
||||||
data[k], data[k+1] = data[k+1], data[k]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid unnecessary recursions of symMerge
|
|
||||||
// by direct insertion of data[m] into data[a:m]
|
|
||||||
// if data[m:b] only contains one element.
|
|
||||||
if b-m == 1 {
|
|
||||||
// Use binary search to find the lowest index i
|
|
||||||
// such that data[i] > data[m] for a <= i < m.
|
|
||||||
// Exit the search loop with i == m in case no such index exists.
|
|
||||||
i := a
|
|
||||||
j := m
|
|
||||||
for i < j {
|
|
||||||
h := int(uint(i+j) >> 1)
|
|
||||||
if !cmpLess(data[m], data[h]) {
|
|
||||||
i = h + 1
|
|
||||||
} else {
|
|
||||||
j = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Swap values until data[m] reaches the position i.
|
|
||||||
for k := m; k > i; k-- {
|
|
||||||
data[k], data[k-1] = data[k-1], data[k]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mid := int(uint(a+b) >> 1)
|
|
||||||
n := mid + m
|
|
||||||
var start, r int
|
|
||||||
if m > mid {
|
|
||||||
start = n - b
|
|
||||||
r = mid
|
|
||||||
} else {
|
|
||||||
start = a
|
|
||||||
r = m
|
|
||||||
}
|
|
||||||
p := n - 1
|
|
||||||
|
|
||||||
for start < r {
|
|
||||||
c := int(uint(start+r) >> 1)
|
|
||||||
if !cmpLess(data[p-c], data[c]) {
|
|
||||||
start = c + 1
|
|
||||||
} else {
|
|
||||||
r = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end := n - start
|
|
||||||
if start < m && m < end {
|
|
||||||
rotateOrdered(data, start, m, end)
|
|
||||||
}
|
|
||||||
if a < start && start < mid {
|
|
||||||
symMergeOrdered(data, a, start, mid)
|
|
||||||
}
|
|
||||||
if mid < end && end < b {
|
|
||||||
symMergeOrdered(data, mid, end, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
|
|
||||||
// Data of the form 'x u v y' is changed to 'x v u y'.
|
|
||||||
// rotate performs at most b-a many calls to data.Swap,
|
|
||||||
// and it assumes non-degenerate arguments: a < m && m < b.
|
|
||||||
func rotateOrdered[E constraints.Ordered](data []E, a, m, b int) {
|
|
||||||
i := m - a
|
|
||||||
j := b - m
|
|
||||||
|
|
||||||
for i != j {
|
|
||||||
if i > j {
|
|
||||||
swapRangeOrdered(data, m-i, m, j)
|
|
||||||
i -= j
|
|
||||||
} else {
|
|
||||||
swapRangeOrdered(data, m-i, m+j-i, i)
|
|
||||||
j -= i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// i == j
|
|
||||||
swapRangeOrdered(data, m-i, m, i)
|
|
||||||
}
|
|
102
vendor/golang.org/x/exp/slog/attr.go
generated
vendored
102
vendor/golang.org/x/exp/slog/attr.go
generated
vendored
|
@ -1,102 +0,0 @@
|
||||||
// Copyright 2022 The Go 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 slog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An Attr is a key-value pair.
|
|
||||||
type Attr struct {
|
|
||||||
Key string
|
|
||||||
Value Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns an Attr for a string value.
|
|
||||||
func String(key, value string) Attr {
|
|
||||||
return Attr{key, StringValue(value)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 returns an Attr for an int64.
|
|
||||||
func Int64(key string, value int64) Attr {
|
|
||||||
return Attr{key, Int64Value(value)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int converts an int to an int64 and returns
|
|
||||||
// an Attr with that value.
|
|
||||||
func Int(key string, value int) Attr {
|
|
||||||
return Int64(key, int64(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 returns an Attr for a uint64.
|
|
||||||
func Uint64(key string, v uint64) Attr {
|
|
||||||
return Attr{key, Uint64Value(v)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 returns an Attr for a floating-point number.
|
|
||||||
func Float64(key string, v float64) Attr {
|
|
||||||
return Attr{key, Float64Value(v)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns an Attr for a bool.
|
|
||||||
func Bool(key string, v bool) Attr {
|
|
||||||
return Attr{key, BoolValue(v)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time returns an Attr for a time.Time.
|
|
||||||
// It discards the monotonic portion.
|
|
||||||
func Time(key string, v time.Time) Attr {
|
|
||||||
return Attr{key, TimeValue(v)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration returns an Attr for a time.Duration.
|
|
||||||
func Duration(key string, v time.Duration) Attr {
|
|
||||||
return Attr{key, DurationValue(v)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group returns an Attr for a Group Value.
|
|
||||||
// The first argument is the key; the remaining arguments
|
|
||||||
// are converted to Attrs as in [Logger.Log].
|
|
||||||
//
|
|
||||||
// Use Group to collect several key-value pairs under a single
|
|
||||||
// key on a log line, or as the result of LogValue
|
|
||||||
// in order to log a single value as multiple Attrs.
|
|
||||||
func Group(key string, args ...any) Attr {
|
|
||||||
return Attr{key, GroupValue(argsToAttrSlice(args)...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func argsToAttrSlice(args []any) []Attr {
|
|
||||||
var (
|
|
||||||
attr Attr
|
|
||||||
attrs []Attr
|
|
||||||
)
|
|
||||||
for len(args) > 0 {
|
|
||||||
attr, args = argsToAttr(args)
|
|
||||||
attrs = append(attrs, attr)
|
|
||||||
}
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any returns an Attr for the supplied value.
|
|
||||||
// See [Value.AnyValue] for how values are treated.
|
|
||||||
func Any(key string, value any) Attr {
|
|
||||||
return Attr{key, AnyValue(value)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal reports whether a and b have equal keys and values.
|
|
||||||
func (a Attr) Equal(b Attr) bool {
|
|
||||||
return a.Key == b.Key && a.Value.Equal(b.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Attr) String() string {
|
|
||||||
return fmt.Sprintf("%s=%s", a.Key, a.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmpty reports whether a has an empty key and a nil value.
|
|
||||||
// That can be written as Attr{} or Any("", nil).
|
|
||||||
func (a Attr) isEmpty() bool {
|
|
||||||
return a.Key == "" && a.Value.num == 0 && a.Value.any == nil
|
|
||||||
}
|
|
316
vendor/golang.org/x/exp/slog/doc.go
generated
vendored
316
vendor/golang.org/x/exp/slog/doc.go
generated
vendored
|
@ -1,316 +0,0 @@
|
||||||
// Copyright 2022 The Go 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 slog provides structured logging,
|
|
||||||
in which log records include a message,
|
|
||||||
a severity level, and various other attributes
|
|
||||||
expressed as key-value pairs.
|
|
||||||
|
|
||||||
It defines a type, [Logger],
|
|
||||||
which provides several methods (such as [Logger.Info] and [Logger.Error])
|
|
||||||
for reporting events of interest.
|
|
||||||
|
|
||||||
Each Logger is associated with a [Handler].
|
|
||||||
A Logger output method creates a [Record] from the method arguments
|
|
||||||
and passes it to the Handler, which decides how to handle it.
|
|
||||||
There is a default Logger accessible through top-level functions
|
|
||||||
(such as [Info] and [Error]) that call the corresponding Logger methods.
|
|
||||||
|
|
||||||
A log record consists of a time, a level, a message, and a set of key-value
|
|
||||||
pairs, where the keys are strings and the values may be of any type.
|
|
||||||
As an example,
|
|
||||||
|
|
||||||
slog.Info("hello", "count", 3)
|
|
||||||
|
|
||||||
creates a record containing the time of the call,
|
|
||||||
a level of Info, the message "hello", and a single
|
|
||||||
pair with key "count" and value 3.
|
|
||||||
|
|
||||||
The [Info] top-level function calls the [Logger.Info] method on the default Logger.
|
|
||||||
In addition to [Logger.Info], there are methods for Debug, Warn and Error levels.
|
|
||||||
Besides these convenience methods for common levels,
|
|
||||||
there is also a [Logger.Log] method which takes the level as an argument.
|
|
||||||
Each of these methods has a corresponding top-level function that uses the
|
|
||||||
default logger.
|
|
||||||
|
|
||||||
The default handler formats the log record's message, time, level, and attributes
|
|
||||||
as a string and passes it to the [log] package.
|
|
||||||
|
|
||||||
2022/11/08 15:28:26 INFO hello count=3
|
|
||||||
|
|
||||||
For more control over the output format, create a logger with a different handler.
|
|
||||||
This statement uses [New] to create a new logger with a TextHandler
|
|
||||||
that writes structured records in text form to standard error:
|
|
||||||
|
|
||||||
logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
|
|
||||||
|
|
||||||
[TextHandler] output is a sequence of key=value pairs, easily and unambiguously
|
|
||||||
parsed by machine. This statement:
|
|
||||||
|
|
||||||
logger.Info("hello", "count", 3)
|
|
||||||
|
|
||||||
produces this output:
|
|
||||||
|
|
||||||
time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3
|
|
||||||
|
|
||||||
The package also provides [JSONHandler], whose output is line-delimited JSON:
|
|
||||||
|
|
||||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
|
||||||
logger.Info("hello", "count", 3)
|
|
||||||
|
|
||||||
produces this output:
|
|
||||||
|
|
||||||
{"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}
|
|
||||||
|
|
||||||
Both [TextHandler] and [JSONHandler] can be configured with [HandlerOptions].
|
|
||||||
There are options for setting the minimum level (see Levels, below),
|
|
||||||
displaying the source file and line of the log call, and
|
|
||||||
modifying attributes before they are logged.
|
|
||||||
|
|
||||||
Setting a logger as the default with
|
|
||||||
|
|
||||||
slog.SetDefault(logger)
|
|
||||||
|
|
||||||
will cause the top-level functions like [Info] to use it.
|
|
||||||
[SetDefault] also updates the default logger used by the [log] package,
|
|
||||||
so that existing applications that use [log.Printf] and related functions
|
|
||||||
will send log records to the logger's handler without needing to be rewritten.
|
|
||||||
|
|
||||||
Some attributes are common to many log calls.
|
|
||||||
For example, you may wish to include the URL or trace identifier of a server request
|
|
||||||
with all log events arising from the request.
|
|
||||||
Rather than repeat the attribute with every log call, you can use [Logger.With]
|
|
||||||
to construct a new Logger containing the attributes:
|
|
||||||
|
|
||||||
logger2 := logger.With("url", r.URL)
|
|
||||||
|
|
||||||
The arguments to With are the same key-value pairs used in [Logger.Info].
|
|
||||||
The result is a new Logger with the same handler as the original, but additional
|
|
||||||
attributes that will appear in the output of every call.
|
|
||||||
|
|
||||||
# Levels
|
|
||||||
|
|
||||||
A [Level] is an integer representing the importance or severity of a log event.
|
|
||||||
The higher the level, the more severe the event.
|
|
||||||
This package defines constants for the most common levels,
|
|
||||||
but any int can be used as a level.
|
|
||||||
|
|
||||||
In an application, you may wish to log messages only at a certain level or greater.
|
|
||||||
One common configuration is to log messages at Info or higher levels,
|
|
||||||
suppressing debug logging until it is needed.
|
|
||||||
The built-in handlers can be configured with the minimum level to output by
|
|
||||||
setting [HandlerOptions.Level].
|
|
||||||
The program's `main` function typically does this.
|
|
||||||
The default value is LevelInfo.
|
|
||||||
|
|
||||||
Setting the [HandlerOptions.Level] field to a [Level] value
|
|
||||||
fixes the handler's minimum level throughout its lifetime.
|
|
||||||
Setting it to a [LevelVar] allows the level to be varied dynamically.
|
|
||||||
A LevelVar holds a Level and is safe to read or write from multiple
|
|
||||||
goroutines.
|
|
||||||
To vary the level dynamically for an entire program, first initialize
|
|
||||||
a global LevelVar:
|
|
||||||
|
|
||||||
var programLevel = new(slog.LevelVar) // Info by default
|
|
||||||
|
|
||||||
Then use the LevelVar to construct a handler, and make it the default:
|
|
||||||
|
|
||||||
h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
|
|
||||||
slog.SetDefault(slog.New(h))
|
|
||||||
|
|
||||||
Now the program can change its logging level with a single statement:
|
|
||||||
|
|
||||||
programLevel.Set(slog.LevelDebug)
|
|
||||||
|
|
||||||
# Groups
|
|
||||||
|
|
||||||
Attributes can be collected into groups.
|
|
||||||
A group has a name that is used to qualify the names of its attributes.
|
|
||||||
How this qualification is displayed depends on the handler.
|
|
||||||
[TextHandler] separates the group and attribute names with a dot.
|
|
||||||
[JSONHandler] treats each group as a separate JSON object, with the group name as the key.
|
|
||||||
|
|
||||||
Use [Group] to create a Group attribute from a name and a list of key-value pairs:
|
|
||||||
|
|
||||||
slog.Group("request",
|
|
||||||
"method", r.Method,
|
|
||||||
"url", r.URL)
|
|
||||||
|
|
||||||
TextHandler would display this group as
|
|
||||||
|
|
||||||
request.method=GET request.url=http://example.com
|
|
||||||
|
|
||||||
JSONHandler would display it as
|
|
||||||
|
|
||||||
"request":{"method":"GET","url":"http://example.com"}
|
|
||||||
|
|
||||||
Use [Logger.WithGroup] to qualify all of a Logger's output
|
|
||||||
with a group name. Calling WithGroup on a Logger results in a
|
|
||||||
new Logger with the same Handler as the original, but with all
|
|
||||||
its attributes qualified by the group name.
|
|
||||||
|
|
||||||
This can help prevent duplicate attribute keys in large systems,
|
|
||||||
where subsystems might use the same keys.
|
|
||||||
Pass each subsystem a different Logger with its own group name so that
|
|
||||||
potential duplicates are qualified:
|
|
||||||
|
|
||||||
logger := slog.Default().With("id", systemID)
|
|
||||||
parserLogger := logger.WithGroup("parser")
|
|
||||||
parseInput(input, parserLogger)
|
|
||||||
|
|
||||||
When parseInput logs with parserLogger, its keys will be qualified with "parser",
|
|
||||||
so even if it uses the common key "id", the log line will have distinct keys.
|
|
||||||
|
|
||||||
# Contexts
|
|
||||||
|
|
||||||
Some handlers may wish to include information from the [context.Context] that is
|
|
||||||
available at the call site. One example of such information
|
|
||||||
is the identifier for the current span when tracing is enabled.
|
|
||||||
|
|
||||||
The [Logger.Log] and [Logger.LogAttrs] methods take a context as a first
|
|
||||||
argument, as do their corresponding top-level functions.
|
|
||||||
|
|
||||||
Although the convenience methods on Logger (Info and so on) and the
|
|
||||||
corresponding top-level functions do not take a context, the alternatives ending
|
|
||||||
in "Context" do. For example,
|
|
||||||
|
|
||||||
slog.InfoContext(ctx, "message")
|
|
||||||
|
|
||||||
It is recommended to pass a context to an output method if one is available.
|
|
||||||
|
|
||||||
# Attrs and Values
|
|
||||||
|
|
||||||
An [Attr] is a key-value pair. The Logger output methods accept Attrs as well as
|
|
||||||
alternating keys and values. The statement
|
|
||||||
|
|
||||||
slog.Info("hello", slog.Int("count", 3))
|
|
||||||
|
|
||||||
behaves the same as
|
|
||||||
|
|
||||||
slog.Info("hello", "count", 3)
|
|
||||||
|
|
||||||
There are convenience constructors for [Attr] such as [Int], [String], and [Bool]
|
|
||||||
for common types, as well as the function [Any] for constructing Attrs of any
|
|
||||||
type.
|
|
||||||
|
|
||||||
The value part of an Attr is a type called [Value].
|
|
||||||
Like an [any], a Value can hold any Go value,
|
|
||||||
but it can represent typical values, including all numbers and strings,
|
|
||||||
without an allocation.
|
|
||||||
|
|
||||||
For the most efficient log output, use [Logger.LogAttrs].
|
|
||||||
It is similar to [Logger.Log] but accepts only Attrs, not alternating
|
|
||||||
keys and values; this allows it, too, to avoid allocation.
|
|
||||||
|
|
||||||
The call
|
|
||||||
|
|
||||||
logger.LogAttrs(nil, slog.LevelInfo, "hello", slog.Int("count", 3))
|
|
||||||
|
|
||||||
is the most efficient way to achieve the same output as
|
|
||||||
|
|
||||||
slog.Info("hello", "count", 3)
|
|
||||||
|
|
||||||
# Customizing a type's logging behavior
|
|
||||||
|
|
||||||
If a type implements the [LogValuer] interface, the [Value] returned from its LogValue
|
|
||||||
method is used for logging. You can use this to control how values of the type
|
|
||||||
appear in logs. For example, you can redact secret information like passwords,
|
|
||||||
or gather a struct's fields in a Group. See the examples under [LogValuer] for
|
|
||||||
details.
|
|
||||||
|
|
||||||
A LogValue method may return a Value that itself implements [LogValuer]. The [Value.Resolve]
|
|
||||||
method handles these cases carefully, avoiding infinite loops and unbounded recursion.
|
|
||||||
Handler authors and others may wish to use Value.Resolve instead of calling LogValue directly.
|
|
||||||
|
|
||||||
# Wrapping output methods
|
|
||||||
|
|
||||||
The logger functions use reflection over the call stack to find the file name
|
|
||||||
and line number of the logging call within the application. This can produce
|
|
||||||
incorrect source information for functions that wrap slog. For instance, if you
|
|
||||||
define this function in file mylog.go:
|
|
||||||
|
|
||||||
func Infof(format string, args ...any) {
|
|
||||||
slog.Default().Info(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
and you call it like this in main.go:
|
|
||||||
|
|
||||||
Infof(slog.Default(), "hello, %s", "world")
|
|
||||||
|
|
||||||
then slog will report the source file as mylog.go, not main.go.
|
|
||||||
|
|
||||||
A correct implementation of Infof will obtain the source location
|
|
||||||
(pc) and pass it to NewRecord.
|
|
||||||
The Infof function in the package-level example called "wrapping"
|
|
||||||
demonstrates how to do this.
|
|
||||||
|
|
||||||
# Working with Records
|
|
||||||
|
|
||||||
Sometimes a Handler will need to modify a Record
|
|
||||||
before passing it on to another Handler or backend.
|
|
||||||
A Record contains a mixture of simple public fields (e.g. Time, Level, Message)
|
|
||||||
and hidden fields that refer to state (such as attributes) indirectly. This
|
|
||||||
means that modifying a simple copy of a Record (e.g. by calling
|
|
||||||
[Record.Add] or [Record.AddAttrs] to add attributes)
|
|
||||||
may have unexpected effects on the original.
|
|
||||||
Before modifying a Record, use [Clone] to
|
|
||||||
create a copy that shares no state with the original,
|
|
||||||
or create a new Record with [NewRecord]
|
|
||||||
and build up its Attrs by traversing the old ones with [Record.Attrs].
|
|
||||||
|
|
||||||
# Performance considerations
|
|
||||||
|
|
||||||
If profiling your application demonstrates that logging is taking significant time,
|
|
||||||
the following suggestions may help.
|
|
||||||
|
|
||||||
If many log lines have a common attribute, use [Logger.With] to create a Logger with
|
|
||||||
that attribute. The built-in handlers will format that attribute only once, at the
|
|
||||||
call to [Logger.With]. The [Handler] interface is designed to allow that optimization,
|
|
||||||
and a well-written Handler should take advantage of it.
|
|
||||||
|
|
||||||
The arguments to a log call are always evaluated, even if the log event is discarded.
|
|
||||||
If possible, defer computation so that it happens only if the value is actually logged.
|
|
||||||
For example, consider the call
|
|
||||||
|
|
||||||
slog.Info("starting request", "url", r.URL.String()) // may compute String unnecessarily
|
|
||||||
|
|
||||||
The URL.String method will be called even if the logger discards Info-level events.
|
|
||||||
Instead, pass the URL directly:
|
|
||||||
|
|
||||||
slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed
|
|
||||||
|
|
||||||
The built-in [TextHandler] will call its String method, but only
|
|
||||||
if the log event is enabled.
|
|
||||||
Avoiding the call to String also preserves the structure of the underlying value.
|
|
||||||
For example [JSONHandler] emits the components of the parsed URL as a JSON object.
|
|
||||||
If you want to avoid eagerly paying the cost of the String call
|
|
||||||
without causing the handler to potentially inspect the structure of the value,
|
|
||||||
wrap the value in a fmt.Stringer implementation that hides its Marshal methods.
|
|
||||||
|
|
||||||
You can also use the [LogValuer] interface to avoid unnecessary work in disabled log
|
|
||||||
calls. Say you need to log some expensive value:
|
|
||||||
|
|
||||||
slog.Debug("frobbing", "value", computeExpensiveValue(arg))
|
|
||||||
|
|
||||||
Even if this line is disabled, computeExpensiveValue will be called.
|
|
||||||
To avoid that, define a type implementing LogValuer:
|
|
||||||
|
|
||||||
type expensive struct { arg int }
|
|
||||||
|
|
||||||
func (e expensive) LogValue() slog.Value {
|
|
||||||
return slog.AnyValue(computeExpensiveValue(e.arg))
|
|
||||||
}
|
|
||||||
|
|
||||||
Then use a value of that type in log calls:
|
|
||||||
|
|
||||||
slog.Debug("frobbing", "value", expensive{arg})
|
|
||||||
|
|
||||||
Now computeExpensiveValue will only be called when the line is enabled.
|
|
||||||
|
|
||||||
The built-in handlers acquire a lock before calling [io.Writer.Write]
|
|
||||||
to ensure that each record is written in one piece. User-defined
|
|
||||||
handlers are responsible for their own locking.
|
|
||||||
*/
|
|
||||||
package slog
|
|
577
vendor/golang.org/x/exp/slog/handler.go
generated
vendored
577
vendor/golang.org/x/exp/slog/handler.go
generated
vendored
|
@ -1,577 +0,0 @@
|
||||||
// Copyright 2022 The Go 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 slog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"golang.org/x/exp/slog/internal/buffer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Handler handles log records produced by a Logger..
|
|
||||||
//
|
|
||||||
// A typical handler may print log records to standard error,
|
|
||||||
// or write them to a file or database, or perhaps augment them
|
|
||||||
// with additional attributes and pass them on to another handler.
|
|
||||||
//
|
|
||||||
// Any of the Handler's methods may be called concurrently with itself
|
|
||||||
// or with other methods. It is the responsibility of the Handler to
|
|
||||||
// manage this concurrency.
|
|
||||||
//
|
|
||||||
// Users of the slog package should not invoke Handler methods directly.
|
|
||||||
// They should use the methods of [Logger] instead.
|
|
||||||
type Handler interface {
|
|
||||||
// Enabled reports whether the handler handles records at the given level.
|
|
||||||
// The handler ignores records whose level is lower.
|
|
||||||
// It is called early, before any arguments are processed,
|
|
||||||
// to save effort if the log event should be discarded.
|
|
||||||
// If called from a Logger method, the first argument is the context
|
|
||||||
// passed to that method, or context.Background() if nil was passed
|
|
||||||
// or the method does not take a context.
|
|
||||||
// The context is passed so Enabled can use its values
|
|
||||||
// to make a decision.
|
|
||||||
Enabled(context.Context, Level) bool
|
|
||||||
|
|
||||||
// Handle handles the Record.
|
|
||||||
// It will only be called when Enabled returns true.
|
|
||||||
// The Context argument is as for Enabled.
|
|
||||||
// It is present solely to provide Handlers access to the context's values.
|
|
||||||
// Canceling the context should not affect record processing.
|
|
||||||
// (Among other things, log messages may be necessary to debug a
|
|
||||||
// cancellation-related problem.)
|
|
||||||
//
|
|
||||||
// Handle methods that produce output should observe the following rules:
|
|
||||||
// - If r.Time is the zero time, ignore the time.
|
|
||||||
// - If r.PC is zero, ignore it.
|
|
||||||
// - Attr's values should be resolved.
|
|
||||||
// - If an Attr's key and value are both the zero value, ignore the Attr.
|
|
||||||
// This can be tested with attr.Equal(Attr{}).
|
|
||||||
// - If a group's key is empty, inline the group's Attrs.
|
|
||||||
// - If a group has no Attrs (even if it has a non-empty key),
|
|
||||||
// ignore it.
|
|
||||||
Handle(context.Context, Record) error
|
|
||||||
|
|
||||||
// WithAttrs returns a new Handler whose attributes consist of
|
|
||||||
// both the receiver's attributes and the arguments.
|
|
||||||
// The Handler owns the slice: it may retain, modify or discard it.
|
|
||||||
WithAttrs(attrs []Attr) Handler
|
|
||||||
|
|
||||||
// WithGroup returns a new Handler with the given group appended to
|
|
||||||
// the receiver's existing groups.
|
|
||||||
// The keys of all subsequent attributes, whether added by With or in a
|
|
||||||
// Record, should be qualified by the sequence of group names.
|
|
||||||
//
|
|
||||||
// How this qualification happens is up to the Handler, so long as
|
|
||||||
// this Handler's attribute keys differ from those of another Handler
|
|
||||||
// with a different sequence of group names.
|
|
||||||
//
|
|
||||||
// A Handler should treat WithGroup as starting a Group of Attrs that ends
|
|
||||||
// at the end of the log event. That is,
|
|
||||||
//
|
|
||||||
// logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2))
|
|
||||||
//
|
|
||||||
// should behave like
|
|
||||||
//
|
|
||||||
// logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2)))
|
|
||||||
//
|
|
||||||
// If the name is empty, WithGroup returns the receiver.
|
|
||||||
WithGroup(name string) Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultHandler struct {
|
|
||||||
ch *commonHandler
|
|
||||||
// log.Output, except for testing
|
|
||||||
output func(calldepth int, message string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDefaultHandler(output func(int, string) error) *defaultHandler {
|
|
||||||
return &defaultHandler{
|
|
||||||
ch: &commonHandler{json: false},
|
|
||||||
output: output,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*defaultHandler) Enabled(_ context.Context, l Level) bool {
|
|
||||||
return l >= LevelInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect the level, attributes and message in a string and
|
|
||||||
// write it with the default log.Logger.
|
|
||||||
// Let the log.Logger handle time and file/line.
|
|
||||||
func (h *defaultHandler) Handle(ctx context.Context, r Record) error {
|
|
||||||
buf := buffer.New()
|
|
||||||
buf.WriteString(r.Level.String())
|
|
||||||
buf.WriteByte(' ')
|
|
||||||
buf.WriteString(r.Message)
|
|
||||||
state := h.ch.newHandleState(buf, true, " ", nil)
|
|
||||||
defer state.free()
|
|
||||||
state.appendNonBuiltIns(r)
|
|
||||||
|
|
||||||
// skip [h.output, defaultHandler.Handle, handlerWriter.Write, log.Output]
|
|
||||||
return h.output(4, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *defaultHandler) WithAttrs(as []Attr) Handler {
|
|
||||||
return &defaultHandler{h.ch.withAttrs(as), h.output}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *defaultHandler) WithGroup(name string) Handler {
|
|
||||||
return &defaultHandler{h.ch.withGroup(name), h.output}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlerOptions are options for a TextHandler or JSONHandler.
|
|
||||||
// A zero HandlerOptions consists entirely of default values.
|
|
||||||
type HandlerOptions struct {
|
|
||||||
// AddSource causes the handler to compute the source code position
|
|
||||||
// of the log statement and add a SourceKey attribute to the output.
|
|
||||||
AddSource bool
|
|
||||||
|
|
||||||
// Level reports the minimum record level that will be logged.
|
|
||||||
// The handler discards records with lower levels.
|
|
||||||
// If Level is nil, the handler assumes LevelInfo.
|
|
||||||
// The handler calls Level.Level for each record processed;
|
|
||||||
// to adjust the minimum level dynamically, use a LevelVar.
|
|
||||||
Level Leveler
|
|
||||||
|
|
||||||
// ReplaceAttr is called to rewrite each non-group attribute before it is logged.
|
|
||||||
// The attribute's value has been resolved (see [Value.Resolve]).
|
|
||||||
// If ReplaceAttr returns an Attr with Key == "", the attribute is discarded.
|
|
||||||
//
|
|
||||||
// The built-in attributes with keys "time", "level", "source", and "msg"
|
|
||||||
// are passed to this function, except that time is omitted
|
|
||||||
// if zero, and source is omitted if AddSource is false.
|
|
||||||
//
|
|
||||||
// The first argument is a list of currently open groups that contain the
|
|
||||||
// Attr. It must not be retained or modified. ReplaceAttr is never called
|
|
||||||
// for Group attributes, only their contents. For example, the attribute
|
|
||||||
// list
|
|
||||||
//
|
|
||||||
// Int("a", 1), Group("g", Int("b", 2)), Int("c", 3)
|
|
||||||
//
|
|
||||||
// results in consecutive calls to ReplaceAttr with the following arguments:
|
|
||||||
//
|
|
||||||
// nil, Int("a", 1)
|
|
||||||
// []string{"g"}, Int("b", 2)
|
|
||||||
// nil, Int("c", 3)
|
|
||||||
//
|
|
||||||
// ReplaceAttr can be used to change the default keys of the built-in
|
|
||||||
// attributes, convert types (for example, to replace a `time.Time` with the
|
|
||||||
// integer seconds since the Unix epoch), sanitize personal information, or
|
|
||||||
// remove attributes from the output.
|
|
||||||
ReplaceAttr func(groups []string, a Attr) Attr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys for "built-in" attributes.
|
|
||||||
const (
|
|
||||||
// TimeKey is the key used by the built-in handlers for the time
|
|
||||||
// when the log method is called. The associated Value is a [time.Time].
|
|
||||||
TimeKey = "time"
|
|
||||||
// LevelKey is the key used by the built-in handlers for the level
|
|
||||||
// of the log call. The associated value is a [Level].
|
|
||||||
LevelKey = "level"
|
|
||||||
// MessageKey is the key used by the built-in handlers for the
|
|
||||||
// message of the log call. The associated value is a string.
|
|
||||||
MessageKey = "msg"
|
|
||||||
// SourceKey is the key used by the built-in handlers for the source file
|
|
||||||
// and line of the log call. The associated value is a string.
|
|
||||||
SourceKey = "source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type commonHandler struct {
|
|
||||||
json bool // true => output JSON; false => output text
|
|
||||||
opts HandlerOptions
|
|
||||||
preformattedAttrs []byte
|
|
||||||
groupPrefix string // for text: prefix of groups opened in preformatting
|
|
||||||
groups []string // all groups started from WithGroup
|
|
||||||
nOpenGroups int // the number of groups opened in preformattedAttrs
|
|
||||||
mu sync.Mutex
|
|
||||||
w io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *commonHandler) clone() *commonHandler {
|
|
||||||
// We can't use assignment because we can't copy the mutex.
|
|
||||||
return &commonHandler{
|
|
||||||
json: h.json,
|
|
||||||
opts: h.opts,
|
|
||||||
preformattedAttrs: slices.Clip(h.preformattedAttrs),
|
|
||||||
groupPrefix: h.groupPrefix,
|
|
||||||
groups: slices.Clip(h.groups),
|
|
||||||
nOpenGroups: h.nOpenGroups,
|
|
||||||
w: h.w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enabled reports whether l is greater than or equal to the
|
|
||||||
// minimum level.
|
|
||||||
func (h *commonHandler) enabled(l Level) bool {
|
|
||||||
minLevel := LevelInfo
|
|
||||||
if h.opts.Level != nil {
|
|
||||||
minLevel = h.opts.Level.Level()
|
|
||||||
}
|
|
||||||
return l >= minLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *commonHandler) withAttrs(as []Attr) *commonHandler {
|
|
||||||
h2 := h.clone()
|
|
||||||
// Pre-format the attributes as an optimization.
|
|
||||||
prefix := buffer.New()
|
|
||||||
defer prefix.Free()
|
|
||||||
prefix.WriteString(h.groupPrefix)
|
|
||||||
state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "", prefix)
|
|
||||||
defer state.free()
|
|
||||||
if len(h2.preformattedAttrs) > 0 {
|
|
||||||
state.sep = h.attrSep()
|
|
||||||
}
|
|
||||||
state.openGroups()
|
|
||||||
for _, a := range as {
|
|
||||||
state.appendAttr(a)
|
|
||||||
}
|
|
||||||
// Remember the new prefix for later keys.
|
|
||||||
h2.groupPrefix = state.prefix.String()
|
|
||||||
// Remember how many opened groups are in preformattedAttrs,
|
|
||||||
// so we don't open them again when we handle a Record.
|
|
||||||
h2.nOpenGroups = len(h2.groups)
|
|
||||||
return h2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *commonHandler) withGroup(name string) *commonHandler {
|
|
||||||
if name == "" {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
h2 := h.clone()
|
|
||||||
h2.groups = append(h2.groups, name)
|
|
||||||
return h2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *commonHandler) handle(r Record) error {
|
|
||||||
state := h.newHandleState(buffer.New(), true, "", nil)
|
|
||||||
defer state.free()
|
|
||||||
if h.json {
|
|
||||||
state.buf.WriteByte('{')
|
|
||||||
}
|
|
||||||
// Built-in attributes. They are not in a group.
|
|
||||||
stateGroups := state.groups
|
|
||||||
state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups.
|
|
||||||
rep := h.opts.ReplaceAttr
|
|
||||||
// time
|
|
||||||
if !r.Time.IsZero() {
|
|
||||||
key := TimeKey
|
|
||||||
val := r.Time.Round(0) // strip monotonic to match Attr behavior
|
|
||||||
if rep == nil {
|
|
||||||
state.appendKey(key)
|
|
||||||
state.appendTime(val)
|
|
||||||
} else {
|
|
||||||
state.appendAttr(Time(key, val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// level
|
|
||||||
key := LevelKey
|
|
||||||
val := r.Level
|
|
||||||
if rep == nil {
|
|
||||||
state.appendKey(key)
|
|
||||||
state.appendString(val.String())
|
|
||||||
} else {
|
|
||||||
state.appendAttr(Any(key, val))
|
|
||||||
}
|
|
||||||
// source
|
|
||||||
if h.opts.AddSource {
|
|
||||||
state.appendAttr(Any(SourceKey, r.source()))
|
|
||||||
}
|
|
||||||
key = MessageKey
|
|
||||||
msg := r.Message
|
|
||||||
if rep == nil {
|
|
||||||
state.appendKey(key)
|
|
||||||
state.appendString(msg)
|
|
||||||
} else {
|
|
||||||
state.appendAttr(String(key, msg))
|
|
||||||
}
|
|
||||||
state.groups = stateGroups // Restore groups passed to ReplaceAttrs.
|
|
||||||
state.appendNonBuiltIns(r)
|
|
||||||
state.buf.WriteByte('\n')
|
|
||||||
|
|
||||||
h.mu.Lock()
|
|
||||||
defer h.mu.Unlock()
|
|
||||||
_, err := h.w.Write(*state.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendNonBuiltIns(r Record) {
|
|
||||||
// preformatted Attrs
|
|
||||||
if len(s.h.preformattedAttrs) > 0 {
|
|
||||||
s.buf.WriteString(s.sep)
|
|
||||||
s.buf.Write(s.h.preformattedAttrs)
|
|
||||||
s.sep = s.h.attrSep()
|
|
||||||
}
|
|
||||||
// Attrs in Record -- unlike the built-in ones, they are in groups started
|
|
||||||
// from WithGroup.
|
|
||||||
s.prefix = buffer.New()
|
|
||||||
defer s.prefix.Free()
|
|
||||||
s.prefix.WriteString(s.h.groupPrefix)
|
|
||||||
s.openGroups()
|
|
||||||
r.Attrs(func(a Attr) bool {
|
|
||||||
s.appendAttr(a)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if s.h.json {
|
|
||||||
// Close all open groups.
|
|
||||||
for range s.h.groups {
|
|
||||||
s.buf.WriteByte('}')
|
|
||||||
}
|
|
||||||
// Close the top-level object.
|
|
||||||
s.buf.WriteByte('}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// attrSep returns the separator between attributes.
|
|
||||||
func (h *commonHandler) attrSep() string {
|
|
||||||
if h.json {
|
|
||||||
return ","
|
|
||||||
}
|
|
||||||
return " "
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleState holds state for a single call to commonHandler.handle.
|
|
||||||
// The initial value of sep determines whether to emit a separator
|
|
||||||
// before the next key, after which it stays true.
|
|
||||||
type handleState struct {
|
|
||||||
h *commonHandler
|
|
||||||
buf *buffer.Buffer
|
|
||||||
freeBuf bool // should buf be freed?
|
|
||||||
sep string // separator to write before next key
|
|
||||||
prefix *buffer.Buffer // for text: key prefix
|
|
||||||
groups *[]string // pool-allocated slice of active groups, for ReplaceAttr
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupPool = sync.Pool{New: func() any {
|
|
||||||
s := make([]string, 0, 10)
|
|
||||||
return &s
|
|
||||||
}}
|
|
||||||
|
|
||||||
func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string, prefix *buffer.Buffer) handleState {
|
|
||||||
s := handleState{
|
|
||||||
h: h,
|
|
||||||
buf: buf,
|
|
||||||
freeBuf: freeBuf,
|
|
||||||
sep: sep,
|
|
||||||
prefix: prefix,
|
|
||||||
}
|
|
||||||
if h.opts.ReplaceAttr != nil {
|
|
||||||
s.groups = groupPool.Get().(*[]string)
|
|
||||||
*s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) free() {
|
|
||||||
if s.freeBuf {
|
|
||||||
s.buf.Free()
|
|
||||||
}
|
|
||||||
if gs := s.groups; gs != nil {
|
|
||||||
*gs = (*gs)[:0]
|
|
||||||
groupPool.Put(gs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) openGroups() {
|
|
||||||
for _, n := range s.h.groups[s.h.nOpenGroups:] {
|
|
||||||
s.openGroup(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separator for group names and keys.
|
|
||||||
const keyComponentSep = '.'
|
|
||||||
|
|
||||||
// openGroup starts a new group of attributes
|
|
||||||
// with the given name.
|
|
||||||
func (s *handleState) openGroup(name string) {
|
|
||||||
if s.h.json {
|
|
||||||
s.appendKey(name)
|
|
||||||
s.buf.WriteByte('{')
|
|
||||||
s.sep = ""
|
|
||||||
} else {
|
|
||||||
s.prefix.WriteString(name)
|
|
||||||
s.prefix.WriteByte(keyComponentSep)
|
|
||||||
}
|
|
||||||
// Collect group names for ReplaceAttr.
|
|
||||||
if s.groups != nil {
|
|
||||||
*s.groups = append(*s.groups, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeGroup ends the group with the given name.
|
|
||||||
func (s *handleState) closeGroup(name string) {
|
|
||||||
if s.h.json {
|
|
||||||
s.buf.WriteByte('}')
|
|
||||||
} else {
|
|
||||||
(*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */]
|
|
||||||
}
|
|
||||||
s.sep = s.h.attrSep()
|
|
||||||
if s.groups != nil {
|
|
||||||
*s.groups = (*s.groups)[:len(*s.groups)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// appendAttr appends the Attr's key and value using app.
|
|
||||||
// It handles replacement and checking for an empty key.
|
|
||||||
// after replacement).
|
|
||||||
func (s *handleState) appendAttr(a Attr) {
|
|
||||||
if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
|
|
||||||
var gs []string
|
|
||||||
if s.groups != nil {
|
|
||||||
gs = *s.groups
|
|
||||||
}
|
|
||||||
// Resolve before calling ReplaceAttr, so the user doesn't have to.
|
|
||||||
a.Value = a.Value.Resolve()
|
|
||||||
a = rep(gs, a)
|
|
||||||
}
|
|
||||||
a.Value = a.Value.Resolve()
|
|
||||||
// Elide empty Attrs.
|
|
||||||
if a.isEmpty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Special case: Source.
|
|
||||||
if v := a.Value; v.Kind() == KindAny {
|
|
||||||
if src, ok := v.Any().(*Source); ok {
|
|
||||||
if s.h.json {
|
|
||||||
a.Value = src.group()
|
|
||||||
} else {
|
|
||||||
a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if a.Value.Kind() == KindGroup {
|
|
||||||
attrs := a.Value.Group()
|
|
||||||
// Output only non-empty groups.
|
|
||||||
if len(attrs) > 0 {
|
|
||||||
// Inline a group with an empty key.
|
|
||||||
if a.Key != "" {
|
|
||||||
s.openGroup(a.Key)
|
|
||||||
}
|
|
||||||
for _, aa := range attrs {
|
|
||||||
s.appendAttr(aa)
|
|
||||||
}
|
|
||||||
if a.Key != "" {
|
|
||||||
s.closeGroup(a.Key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.appendKey(a.Key)
|
|
||||||
s.appendValue(a.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendError(err error) {
|
|
||||||
s.appendString(fmt.Sprintf("!ERROR:%v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendKey(key string) {
|
|
||||||
s.buf.WriteString(s.sep)
|
|
||||||
if s.prefix != nil {
|
|
||||||
// TODO: optimize by avoiding allocation.
|
|
||||||
s.appendString(string(*s.prefix) + key)
|
|
||||||
} else {
|
|
||||||
s.appendString(key)
|
|
||||||
}
|
|
||||||
if s.h.json {
|
|
||||||
s.buf.WriteByte(':')
|
|
||||||
} else {
|
|
||||||
s.buf.WriteByte('=')
|
|
||||||
}
|
|
||||||
s.sep = s.h.attrSep()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendString(str string) {
|
|
||||||
if s.h.json {
|
|
||||||
s.buf.WriteByte('"')
|
|
||||||
*s.buf = appendEscapedJSONString(*s.buf, str)
|
|
||||||
s.buf.WriteByte('"')
|
|
||||||
} else {
|
|
||||||
// text
|
|
||||||
if needsQuoting(str) {
|
|
||||||
*s.buf = strconv.AppendQuote(*s.buf, str)
|
|
||||||
} else {
|
|
||||||
s.buf.WriteString(str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendValue(v Value) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
// If it panics with a nil pointer, the most likely cases are
|
|
||||||
// an encoding.TextMarshaler or error fails to guard against nil,
|
|
||||||
// in which case "<nil>" seems to be the feasible choice.
|
|
||||||
//
|
|
||||||
// Adapted from the code in fmt/print.go.
|
|
||||||
if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() {
|
|
||||||
s.appendString("<nil>")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise just print the original panic message.
|
|
||||||
s.appendString(fmt.Sprintf("!PANIC: %v", r))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if s.h.json {
|
|
||||||
err = appendJSONValue(s, v)
|
|
||||||
} else {
|
|
||||||
err = appendTextValue(s, v)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
s.appendError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *handleState) appendTime(t time.Time) {
|
|
||||||
if s.h.json {
|
|
||||||
appendJSONTime(s, t)
|
|
||||||
} else {
|
|
||||||
writeTimeRFC3339Millis(s.buf, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This takes half the time of Time.AppendFormat.
|
|
||||||
func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) {
|
|
||||||
year, month, day := t.Date()
|
|
||||||
buf.WritePosIntWidth(year, 4)
|
|
||||||
buf.WriteByte('-')
|
|
||||||
buf.WritePosIntWidth(int(month), 2)
|
|
||||||
buf.WriteByte('-')
|
|
||||||
buf.WritePosIntWidth(day, 2)
|
|
||||||
buf.WriteByte('T')
|
|
||||||
hour, min, sec := t.Clock()
|
|
||||||
buf.WritePosIntWidth(hour, 2)
|
|
||||||
buf.WriteByte(':')
|
|
||||||
buf.WritePosIntWidth(min, 2)
|
|
||||||
buf.WriteByte(':')
|
|
||||||
buf.WritePosIntWidth(sec, 2)
|
|
||||||
ns := t.Nanosecond()
|
|
||||||
buf.WriteByte('.')
|
|
||||||
buf.WritePosIntWidth(ns/1e6, 3)
|
|
||||||
_, offsetSeconds := t.Zone()
|
|
||||||
if offsetSeconds == 0 {
|
|
||||||
buf.WriteByte('Z')
|
|
||||||
} else {
|
|
||||||
offsetMinutes := offsetSeconds / 60
|
|
||||||
if offsetMinutes < 0 {
|
|
||||||
buf.WriteByte('-')
|
|
||||||
offsetMinutes = -offsetMinutes
|
|
||||||
} else {
|
|
||||||
buf.WriteByte('+')
|
|
||||||
}
|
|
||||||
buf.WritePosIntWidth(offsetMinutes/60, 2)
|
|
||||||
buf.WriteByte(':')
|
|
||||||
buf.WritePosIntWidth(offsetMinutes%60, 2)
|
|
||||||
}
|
|
||||||
}
|
|
84
vendor/golang.org/x/exp/slog/internal/buffer/buffer.go
generated
vendored
84
vendor/golang.org/x/exp/slog/internal/buffer/buffer.go
generated
vendored
|
@ -1,84 +0,0 @@
|
||||||
// Copyright 2022 The Go 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 buffer provides a pool-allocated byte buffer.
|
|
||||||
package buffer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Buffer adapted from go/src/fmt/print.go
|
|
||||||
type Buffer []byte
|
|
||||||
|
|
||||||
// Having an initial size gives a dramatic speedup.
|
|
||||||
var bufPool = sync.Pool{
|
|
||||||
New: func() any {
|
|
||||||
b := make([]byte, 0, 1024)
|
|
||||||
return (*Buffer)(&b)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *Buffer {
|
|
||||||
return bufPool.Get().(*Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) Free() {
|
|
||||||
// To reduce peak allocation, return only smaller buffers to the pool.
|
|
||||||
const maxBufferSize = 16 << 10
|
|
||||||
if cap(*b) <= maxBufferSize {
|
|
||||||
*b = (*b)[:0]
|
|
||||||
bufPool.Put(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) Reset() {
|
|
||||||
*b = (*b)[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) Write(p []byte) (int, error) {
|
|
||||||
*b = append(*b, p...)
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) WriteString(s string) {
|
|
||||||
*b = append(*b, s...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) WriteByte(c byte) {
|
|
||||||
*b = append(*b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) WritePosInt(i int) {
|
|
||||||
b.WritePosIntWidth(i, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WritePosIntWidth writes non-negative integer i to the buffer, padded on the left
|
|
||||||
// by zeroes to the given width. Use a width of 0 to omit padding.
|
|
||||||
func (b *Buffer) WritePosIntWidth(i, width int) {
|
|
||||||
// Cheap integer to fixed-width decimal ASCII.
|
|
||||||
// Copied from log/log.go.
|
|
||||||
|
|
||||||
if i < 0 {
|
|
||||||
panic("negative int")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble decimal in reverse order.
|
|
||||||
var bb [20]byte
|
|
||||||
bp := len(bb) - 1
|
|
||||||
for i >= 10 || width > 1 {
|
|
||||||
width--
|
|
||||||
q := i / 10
|
|
||||||
bb[bp] = byte('0' + i - q*10)
|
|
||||||
bp--
|
|
||||||
i = q
|
|
||||||
}
|
|
||||||
// i < 10
|
|
||||||
bb[bp] = byte('0' + i)
|
|
||||||
b.Write(bb[bp:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) String() string {
|
|
||||||
return string(*b)
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue