Copied mongo repo (to patch it)
This commit is contained in:
34
mongo/internal/background_context.go
Normal file
34
mongo/internal/background_context.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import "context"
|
||||
|
||||
// backgroundContext is an implementation of the context.Context interface that wraps a child Context. Value requests
|
||||
// are forwarded to the child Context but the Done and Err functions are overridden to ensure the new context does not
|
||||
// time out or get cancelled.
|
||||
type backgroundContext struct {
|
||||
context.Context
|
||||
childValuesCtx context.Context
|
||||
}
|
||||
|
||||
// NewBackgroundContext creates a new Context whose behavior matches that of context.Background(), but Value calls are
|
||||
// forwarded to the provided ctx parameter. If ctx is nil, context.Background() is returned.
|
||||
func NewBackgroundContext(ctx context.Context) context.Context {
|
||||
if ctx == nil {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
return &backgroundContext{
|
||||
Context: context.Background(),
|
||||
childValuesCtx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backgroundContext) Value(key interface{}) interface{} {
|
||||
return b.childValuesCtx.Value(key)
|
||||
}
|
58
mongo/internal/background_context_test.go
Normal file
58
mongo/internal/background_context_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/internal/testutil/assert"
|
||||
)
|
||||
|
||||
func TestBackgroundContext(t *testing.T) {
|
||||
t.Run("NewBackgroundContext accepts a nil child", func(t *testing.T) {
|
||||
ctx := NewBackgroundContext(nil)
|
||||
assert.Equal(t, context.Background(), ctx, "expected context.Background() for a nil child")
|
||||
})
|
||||
t.Run("Value requests are forwarded", func(t *testing.T) {
|
||||
// Tests the Value function.
|
||||
|
||||
type ctxKey struct{}
|
||||
expectedVal := "value"
|
||||
childCtx := context.WithValue(context.Background(), ctxKey{}, expectedVal)
|
||||
|
||||
ctx := NewBackgroundContext(childCtx)
|
||||
gotVal, ok := ctx.Value(ctxKey{}).(string)
|
||||
assert.True(t, ok, "expected context to contain a string value for ctxKey")
|
||||
assert.Equal(t, expectedVal, gotVal, "expected value for ctxKey to be %q, got %q", expectedVal, gotVal)
|
||||
})
|
||||
t.Run("background context does not have a deadline", func(t *testing.T) {
|
||||
// Tests the Deadline function.
|
||||
|
||||
childCtx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
ctx := NewBackgroundContext(childCtx)
|
||||
deadline, ok := ctx.Deadline()
|
||||
assert.False(t, ok, "expected context to have no deadline, but got %v", deadline)
|
||||
})
|
||||
t.Run("background context cannot be cancelled", func(t *testing.T) {
|
||||
// Tests the Done and Err functions.
|
||||
|
||||
childCtx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
ctx := NewBackgroundContext(childCtx)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("expected context to not expire, but Done channel had a value; ctx error: %v", ctx.Err())
|
||||
default:
|
||||
}
|
||||
assert.Nil(t, ctx.Err(), "expected context error to be nil, got %v", ctx.Err())
|
||||
})
|
||||
}
|
47
mongo/internal/cancellation_listener.go
Normal file
47
mongo/internal/cancellation_listener.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import "context"
|
||||
|
||||
// CancellationListener listens for context cancellation in a loop until the context expires or the listener is aborted.
|
||||
type CancellationListener struct {
|
||||
aborted bool
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// NewCancellationListener constructs a CancellationListener.
|
||||
func NewCancellationListener() *CancellationListener {
|
||||
return &CancellationListener{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Listen blocks until the provided context is cancelled or listening is aborted via the StopListening function. If this
|
||||
// detects that the context has been cancelled (i.e. ctx.Err() == context.Canceled), the provided callback is called to
|
||||
// abort in-progress work. Even if the context expires, this function will block until StopListening is called.
|
||||
func (c *CancellationListener) Listen(ctx context.Context, abortFn func()) {
|
||||
c.aborted = false
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if ctx.Err() == context.Canceled {
|
||||
c.aborted = true
|
||||
abortFn()
|
||||
}
|
||||
|
||||
<-c.done
|
||||
case <-c.done:
|
||||
}
|
||||
}
|
||||
|
||||
// StopListening stops the in-progress Listen call. This blocks if there is no in-progress Listen call. This function
|
||||
// will return true if the provided abort callback was called when listening for cancellation on the previous context.
|
||||
func (c *CancellationListener) StopListening() bool {
|
||||
c.done <- struct{}{}
|
||||
return c.aborted
|
||||
}
|
19
mongo/internal/const.go
Normal file
19
mongo/internal/const.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal // import "go.mongodb.org/mongo-driver/internal"
|
||||
|
||||
// Version is the current version of the driver.
|
||||
var Version = "local build"
|
||||
|
||||
// LegacyHello is the legacy version of the hello command.
|
||||
var LegacyHello = "isMaster"
|
||||
|
||||
// LegacyHelloLowercase is the lowercase, legacy version of the hello command.
|
||||
var LegacyHelloLowercase = "ismaster"
|
||||
|
||||
// LegacyNotPrimary is the legacy version of the "not primary" server error message.
|
||||
var LegacyNotPrimary = "not master"
|
39
mongo/internal/csfle_util.go
Normal file
39
mongo/internal/csfle_util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
|
||||
)
|
||||
|
||||
const (
|
||||
EncryptedCacheCollection = "ecc"
|
||||
EncryptedStateCollection = "esc"
|
||||
EncryptedCompactionCollection = "ecoc"
|
||||
)
|
||||
|
||||
// GetEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
|
||||
func GetEncryptedStateCollectionName(efBSON bsoncore.Document, dataCollectionName string, stateCollection string) (string, error) {
|
||||
fieldName := stateCollection + "Collection"
|
||||
val, err := efBSON.LookupErr(fieldName)
|
||||
if err != nil {
|
||||
if err != bsoncore.ErrElementNotFound {
|
||||
return "", err
|
||||
}
|
||||
// Return default name.
|
||||
defaultName := "enxcol_." + dataCollectionName + "." + stateCollection
|
||||
return defaultName, nil
|
||||
}
|
||||
|
||||
stateCollectionName, ok := val.StringValueOK()
|
||||
if !ok {
|
||||
return "", fmt.Errorf("expected string for '%v', got: %v", fieldName, val.Type)
|
||||
}
|
||||
return stateCollectionName, nil
|
||||
}
|
58
mongo/internal/csot_util.go
Normal file
58
mongo/internal/csot_util.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timeoutKey struct{}
|
||||
|
||||
// MakeTimeoutContext returns a new context with Client-Side Operation Timeout (CSOT) feature-gated behavior
|
||||
// and a Timeout set to the passed in Duration. Setting a Timeout on a single operation is not supported in
|
||||
// public API.
|
||||
//
|
||||
// TODO(GODRIVER-2348) We may be able to remove this function once CSOT feature-gated behavior becomes the
|
||||
// TODO default behavior.
|
||||
func MakeTimeoutContext(ctx context.Context, to time.Duration) (context.Context, context.CancelFunc) {
|
||||
// Only use the passed in Duration as a timeout on the Context if it
|
||||
// is non-zero.
|
||||
cancelFunc := func() {}
|
||||
if to != 0 {
|
||||
ctx, cancelFunc = context.WithTimeout(ctx, to)
|
||||
}
|
||||
return context.WithValue(ctx, timeoutKey{}, true), cancelFunc
|
||||
}
|
||||
|
||||
func IsTimeoutContext(ctx context.Context) bool {
|
||||
return ctx.Value(timeoutKey{}) != nil
|
||||
}
|
||||
|
||||
// ZeroRTTMonitor implements the RTTMonitor interface and is used internally for testing. It returns 0 for all
|
||||
// RTT calculations and an empty string for RTT statistics.
|
||||
type ZeroRTTMonitor struct{}
|
||||
|
||||
// EWMA implements the RTT monitor interface.
|
||||
func (zrm *ZeroRTTMonitor) EWMA() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Min implements the RTT monitor interface.
|
||||
func (zrm *ZeroRTTMonitor) Min() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
// P90 implements the RTT monitor interface.
|
||||
func (zrm *ZeroRTTMonitor) P90() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Stats implements the RTT monitor interface.
|
||||
func (zrm *ZeroRTTMonitor) Stats() string {
|
||||
return ""
|
||||
}
|
123
mongo/internal/error.go
Normal file
123
mongo/internal/error.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// WrappedError represents an error that contains another error.
|
||||
type WrappedError interface {
|
||||
// Message gets the basic message of the error.
|
||||
Message() string
|
||||
// Inner gets the inner error if one exists.
|
||||
Inner() error
|
||||
}
|
||||
|
||||
// RolledUpErrorMessage gets a flattened error message.
|
||||
func RolledUpErrorMessage(err error) string {
|
||||
if wrappedErr, ok := err.(WrappedError); ok {
|
||||
inner := wrappedErr.Inner()
|
||||
if inner != nil {
|
||||
return fmt.Sprintf("%s: %s", wrappedErr.Message(), RolledUpErrorMessage(inner))
|
||||
}
|
||||
|
||||
return wrappedErr.Message()
|
||||
}
|
||||
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// UnwrapError attempts to unwrap the error down to its root cause.
|
||||
func UnwrapError(err error) error {
|
||||
|
||||
switch tErr := err.(type) {
|
||||
case WrappedError:
|
||||
return UnwrapError(tErr.Inner())
|
||||
case *multiError:
|
||||
return UnwrapError(tErr.errors[0])
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// WrapError wraps an error with a message.
|
||||
func WrapError(inner error, message string) error {
|
||||
return &wrappedError{message, inner}
|
||||
}
|
||||
|
||||
// WrapErrorf wraps an error with a message.
|
||||
func WrapErrorf(inner error, format string, args ...interface{}) error {
|
||||
return &wrappedError{fmt.Sprintf(format, args...), inner}
|
||||
}
|
||||
|
||||
// MultiError combines multiple errors into a single error. If there are no errors,
|
||||
// nil is returned. If there is 1 error, it is returned. Otherwise, they are combined.
|
||||
func MultiError(errors ...error) error {
|
||||
|
||||
// remove nils from the error list
|
||||
var nonNils []error
|
||||
for _, e := range errors {
|
||||
if e != nil {
|
||||
nonNils = append(nonNils, e)
|
||||
}
|
||||
}
|
||||
|
||||
switch len(nonNils) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return nonNils[0]
|
||||
default:
|
||||
return &multiError{
|
||||
message: "multiple errors encountered",
|
||||
errors: nonNils,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type multiError struct {
|
||||
message string
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (e *multiError) Message() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e *multiError) Error() string {
|
||||
result := e.message
|
||||
for _, e := range e.errors {
|
||||
result += fmt.Sprintf("\n %s", e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (e *multiError) Errors() []error {
|
||||
return e.errors
|
||||
}
|
||||
|
||||
type wrappedError struct {
|
||||
message string
|
||||
inner error
|
||||
}
|
||||
|
||||
func (e *wrappedError) Message() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e *wrappedError) Error() string {
|
||||
return RolledUpErrorMessage(e)
|
||||
}
|
||||
|
||||
func (e *wrappedError) Inner() error {
|
||||
return e.inner
|
||||
}
|
||||
|
||||
func (e *wrappedError) Unwrap() error {
|
||||
return e.inner
|
||||
}
|
38
mongo/internal/http.go
Normal file
38
mongo/internal/http.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal // import "go.mongodb.org/mongo-driver/internal"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultHTTPClient is the default HTTP client used across the driver.
|
||||
var DefaultHTTPClient = &http.Client{
|
||||
// TODO(GODRIVER-2623): Use "http.DefaultTransport.Clone" once we change the minimum supported Go version to 1.13.
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// CloseIdleHTTPConnections closes any connections which were previously
|
||||
// connected from previous requests but are now sitting idle in
|
||||
// a "keep-alive" state. It does not interrupt any connections currently
|
||||
// in use.
|
||||
// Borrowed from go standard library.
|
||||
func CloseIdleHTTPConnections(client *http.Client) {
|
||||
type closeIdler interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
if tr, ok := client.Transport.(closeIdler); ok {
|
||||
tr.CloseIdleConnections()
|
||||
}
|
||||
}
|
38
mongo/internal/randutil/rand/bits.go
Normal file
38
mongo/internal/randutil/rand/bits.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copied from https://cs.opensource.google/go/go/+/946b4baaf6521d521928500b2b57429c149854e7:src/math/bits.go
|
||||
|
||||
// Copyright 2017 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 rand
|
||||
|
||||
// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.
|
||||
// The carry input must be 0 or 1; otherwise the behavior is undefined.
|
||||
// The carryOut output is guaranteed to be 0 or 1.
|
||||
func Add64(x, y, carry uint64) (sum, carryOut uint64) {
|
||||
yc := y + carry
|
||||
sum = x + yc
|
||||
if sum < x || yc < y {
|
||||
carryOut = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y
|
||||
// with the product bits' upper half returned in hi and the lower
|
||||
// half returned in lo.
|
||||
func Mul64(x, y uint64) (hi, lo uint64) {
|
||||
const mask32 = 1<<32 - 1
|
||||
x0 := x & mask32
|
||||
x1 := x >> 32
|
||||
y0 := y & mask32
|
||||
y1 := y >> 32
|
||||
w0 := x0 * y0
|
||||
t := x1*y0 + w0>>32
|
||||
w1 := t & mask32
|
||||
w2 := t >> 32
|
||||
w1 += x0 * y1
|
||||
hi = x1*y1 + w2 + w1>>32
|
||||
lo = x * y
|
||||
return
|
||||
}
|
223
mongo/internal/randutil/rand/exp.go
Normal file
223
mongo/internal/randutil/rand/exp.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/exp.go
|
||||
|
||||
// Copyright 2009 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 rand
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
* Exponential distribution
|
||||
*
|
||||
* See "The Ziggurat Method for Generating Random Variables"
|
||||
* (Marsaglia & Tsang, 2000)
|
||||
* http://www.jstatsoft.org/v05/i08/paper [pdf]
|
||||
*/
|
||||
|
||||
const (
|
||||
re = 7.69711747013104972
|
||||
)
|
||||
|
||||
// ExpFloat64 returns an exponentially distributed float64 in the range
|
||||
// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter
|
||||
// (lambda) is 1 and whose mean is 1/lambda (1).
|
||||
// To produce a distribution with a different rate parameter,
|
||||
// callers can adjust the output using:
|
||||
//
|
||||
// sample = ExpFloat64() / desiredRateParameter
|
||||
func (r *Rand) ExpFloat64() float64 {
|
||||
for {
|
||||
j := r.Uint32()
|
||||
i := j & 0xFF
|
||||
x := float64(j) * float64(we[i])
|
||||
if j < ke[i] {
|
||||
return x
|
||||
}
|
||||
if i == 0 {
|
||||
return re - math.Log(r.Float64())
|
||||
}
|
||||
if fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) {
|
||||
return x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ke = [256]uint32{
|
||||
0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
|
||||
0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
|
||||
0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78,
|
||||
0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651,
|
||||
0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca,
|
||||
0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8,
|
||||
0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea,
|
||||
0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba,
|
||||
0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed,
|
||||
0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662,
|
||||
0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3,
|
||||
0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace,
|
||||
0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6,
|
||||
0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7,
|
||||
0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415,
|
||||
0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4,
|
||||
0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36,
|
||||
0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46,
|
||||
0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac,
|
||||
0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245,
|
||||
0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52,
|
||||
0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06,
|
||||
0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0,
|
||||
0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9,
|
||||
0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76,
|
||||
0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516,
|
||||
0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289,
|
||||
0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed,
|
||||
0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb,
|
||||
0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e,
|
||||
0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a,
|
||||
0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1,
|
||||
0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b,
|
||||
0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621,
|
||||
0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d,
|
||||
0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3,
|
||||
0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73,
|
||||
0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88,
|
||||
0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a,
|
||||
0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb,
|
||||
0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176,
|
||||
0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be,
|
||||
0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192,
|
||||
0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed,
|
||||
0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936,
|
||||
0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b,
|
||||
0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4,
|
||||
0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1,
|
||||
0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482,
|
||||
0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023,
|
||||
0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
|
||||
0xe6da6ecf,
|
||||
}
|
||||
var we = [256]float32{
|
||||
2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
|
||||
3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
|
||||
5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11,
|
||||
7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11,
|
||||
9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10,
|
||||
1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10,
|
||||
1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10,
|
||||
1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10,
|
||||
1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10,
|
||||
1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10,
|
||||
1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10,
|
||||
1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10,
|
||||
1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10,
|
||||
1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10,
|
||||
2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10,
|
||||
2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10,
|
||||
2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10,
|
||||
2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10,
|
||||
2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10,
|
||||
2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10,
|
||||
2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10,
|
||||
2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10,
|
||||
2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10,
|
||||
2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10,
|
||||
3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10,
|
||||
3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10,
|
||||
3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10,
|
||||
3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10,
|
||||
3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10,
|
||||
3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10,
|
||||
3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10,
|
||||
3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10,
|
||||
3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10,
|
||||
4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10,
|
||||
4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10,
|
||||
4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10,
|
||||
4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10,
|
||||
4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10,
|
||||
4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10,
|
||||
4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10,
|
||||
4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10,
|
||||
5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10,
|
||||
5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10,
|
||||
5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10,
|
||||
5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10,
|
||||
5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10,
|
||||
5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10,
|
||||
6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10,
|
||||
6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10,
|
||||
6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10,
|
||||
6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10,
|
||||
6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10,
|
||||
7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10,
|
||||
7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10,
|
||||
7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10,
|
||||
8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10,
|
||||
8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10,
|
||||
8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10,
|
||||
9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10,
|
||||
9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09,
|
||||
1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09,
|
||||
1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09,
|
||||
1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
|
||||
1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
|
||||
}
|
||||
var fe = [256]float32{
|
||||
1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
|
||||
0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
|
||||
0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665,
|
||||
0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967,
|
||||
0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896,
|
||||
0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092,
|
||||
0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386,
|
||||
0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495,
|
||||
0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752,
|
||||
0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325,
|
||||
0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955,
|
||||
0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694,
|
||||
0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218,
|
||||
0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763,
|
||||
0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044,
|
||||
0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796,
|
||||
0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408,
|
||||
0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928,
|
||||
0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393,
|
||||
0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625,
|
||||
0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107,
|
||||
0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878,
|
||||
0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438,
|
||||
0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682,
|
||||
0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852,
|
||||
0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479,
|
||||
0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354,
|
||||
0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494,
|
||||
0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119,
|
||||
0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624,
|
||||
0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574,
|
||||
0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672,
|
||||
0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763,
|
||||
0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816,
|
||||
0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919,
|
||||
0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274,
|
||||
0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195,
|
||||
0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106,
|
||||
0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434,
|
||||
0.062193416, 0.060783047, 0.059384305, 0.057997175,
|
||||
0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236,
|
||||
0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623,
|
||||
0.043502413, 0.042254124, 0.041017443, 0.039792392,
|
||||
0.038578995, 0.037377283, 0.036187284, 0.035009038,
|
||||
0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566,
|
||||
0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421,
|
||||
0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867,
|
||||
0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392,
|
||||
0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414,
|
||||
0.008780315, 0.007963077, 0.0071633533, 0.006381906,
|
||||
0.0056196423, 0.0048776558, 0.004157295, 0.0034602648,
|
||||
0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693,
|
||||
0.00045413437,
|
||||
}
|
158
mongo/internal/randutil/rand/normal.go
Normal file
158
mongo/internal/randutil/rand/normal.go
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/normal.go
|
||||
|
||||
// Copyright 2009 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 rand
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
* Normal distribution
|
||||
*
|
||||
* See "The Ziggurat Method for Generating Random Variables"
|
||||
* (Marsaglia & Tsang, 2000)
|
||||
* http://www.jstatsoft.org/v05/i08/paper [pdf]
|
||||
*/
|
||||
|
||||
const (
|
||||
rn = 3.442619855899
|
||||
)
|
||||
|
||||
func absInt32(i int32) uint32 {
|
||||
if i < 0 {
|
||||
return uint32(-i)
|
||||
}
|
||||
return uint32(i)
|
||||
}
|
||||
|
||||
// NormFloat64 returns a normally distributed float64 in the range
|
||||
// [-math.MaxFloat64, +math.MaxFloat64] with
|
||||
// standard normal distribution (mean = 0, stddev = 1).
|
||||
// To produce a different normal distribution, callers can
|
||||
// adjust the output using:
|
||||
//
|
||||
// sample = NormFloat64() * desiredStdDev + desiredMean
|
||||
func (r *Rand) NormFloat64() float64 {
|
||||
for {
|
||||
j := int32(r.Uint32()) // Possibly negative
|
||||
i := j & 0x7F
|
||||
x := float64(j) * float64(wn[i])
|
||||
if absInt32(j) < kn[i] {
|
||||
// This case should be hit better than 99% of the time.
|
||||
return x
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// This extra work is only required for the base strip.
|
||||
for {
|
||||
x = -math.Log(r.Float64()) * (1.0 / rn)
|
||||
y := -math.Log(r.Float64())
|
||||
if y+y >= x*x {
|
||||
break
|
||||
}
|
||||
}
|
||||
if j > 0 {
|
||||
return rn + x
|
||||
}
|
||||
return -rn - x
|
||||
}
|
||||
if fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) {
|
||||
return x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var kn = [128]uint32{
|
||||
0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2,
|
||||
0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,
|
||||
0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7,
|
||||
0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883,
|
||||
0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30,
|
||||
0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa,
|
||||
0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d,
|
||||
0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18,
|
||||
0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924,
|
||||
0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a,
|
||||
0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4,
|
||||
0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62,
|
||||
0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e,
|
||||
0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473,
|
||||
0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd,
|
||||
0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568,
|
||||
0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08,
|
||||
0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc,
|
||||
0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94,
|
||||
0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb,
|
||||
0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075,
|
||||
0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba,
|
||||
0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded,
|
||||
0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72,
|
||||
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
|
||||
0x7ba90bdc, 0x7a722176, 0x77d664e5,
|
||||
}
|
||||
var wn = [128]float32{
|
||||
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
|
||||
2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,
|
||||
2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10,
|
||||
3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10,
|
||||
3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10,
|
||||
4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10,
|
||||
4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10,
|
||||
4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10,
|
||||
5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10,
|
||||
5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10,
|
||||
5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10,
|
||||
5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10,
|
||||
6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10,
|
||||
6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10,
|
||||
6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10,
|
||||
6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10,
|
||||
7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10,
|
||||
7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10,
|
||||
7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10,
|
||||
7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10,
|
||||
8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10,
|
||||
8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10,
|
||||
8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10,
|
||||
9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10,
|
||||
9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10,
|
||||
9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09,
|
||||
1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09,
|
||||
1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09,
|
||||
1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09,
|
||||
1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09,
|
||||
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
|
||||
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
|
||||
}
|
||||
var fn = [128]float32{
|
||||
1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303,
|
||||
0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177,
|
||||
0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569,
|
||||
0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277,
|
||||
0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434,
|
||||
0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903,
|
||||
0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055,
|
||||
0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766,
|
||||
0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872,
|
||||
0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642,
|
||||
0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446,
|
||||
0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685,
|
||||
0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484,
|
||||
0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807,
|
||||
0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239,
|
||||
0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877,
|
||||
0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499,
|
||||
0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037,
|
||||
0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265,
|
||||
0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602,
|
||||
0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467,
|
||||
0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058,
|
||||
0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863,
|
||||
0.040742867, 0.03688439, 0.033087887, 0.029356318,
|
||||
0.025693292, 0.022103304, 0.018592102, 0.015167298,
|
||||
0.011839478, 0.008624485, 0.005548995, 0.0026696292,
|
||||
}
|
374
mongo/internal/randutil/rand/rand.go
Normal file
374
mongo/internal/randutil/rand/rand.go
Normal file
@@ -0,0 +1,374 @@
|
||||
// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/rand.go
|
||||
|
||||
// Copyright 2009 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 rand implements pseudo-random number generators.
|
||||
//
|
||||
// Random numbers are generated by a Source. Top-level functions, such as
|
||||
// Float64 and Int, use a default shared Source that produces a deterministic
|
||||
// sequence of values each time a program is run. Use the Seed function to
|
||||
// initialize the default Source if different behavior is required for each run.
|
||||
// The default Source, a LockedSource, is safe for concurrent use by multiple
|
||||
// goroutines, but Sources created by NewSource are not. However, Sources are small
|
||||
// and it is reasonable to have a separate Source for each goroutine, seeded
|
||||
// differently, to avoid locking.
|
||||
//
|
||||
// For random numbers suitable for security-sensitive work, see the crypto/rand
|
||||
// package.
|
||||
package rand
|
||||
|
||||
import "sync"
|
||||
|
||||
// A Source represents a source of uniformly-distributed
|
||||
// pseudo-random int64 values in the range [0, 1<<64).
|
||||
type Source interface {
|
||||
Uint64() uint64
|
||||
Seed(seed uint64)
|
||||
}
|
||||
|
||||
// NewSource returns a new pseudo-random Source seeded with the given value.
|
||||
func NewSource(seed uint64) Source {
|
||||
var rng PCGSource
|
||||
rng.Seed(seed)
|
||||
return &rng
|
||||
}
|
||||
|
||||
// A Rand is a source of random numbers.
|
||||
type Rand struct {
|
||||
src Source
|
||||
|
||||
// readVal contains remainder of 64-bit integer used for bytes
|
||||
// generation during most recent Read call.
|
||||
// It is saved so next Read call can start where the previous
|
||||
// one finished.
|
||||
readVal uint64
|
||||
// readPos indicates the number of low-order bytes of readVal
|
||||
// that are still valid.
|
||||
readPos int8
|
||||
}
|
||||
|
||||
// New returns a new Rand that uses random values from src
|
||||
// to generate other random values.
|
||||
func New(src Source) *Rand {
|
||||
return &Rand{src: src}
|
||||
}
|
||||
|
||||
// Seed uses the provided seed value to initialize the generator to a deterministic state.
|
||||
// Seed should not be called concurrently with any other Rand method.
|
||||
func (r *Rand) Seed(seed uint64) {
|
||||
if lk, ok := r.src.(*LockedSource); ok {
|
||||
lk.seedPos(seed, &r.readPos)
|
||||
return
|
||||
}
|
||||
|
||||
r.src.Seed(seed)
|
||||
r.readPos = 0
|
||||
}
|
||||
|
||||
// Uint64 returns a pseudo-random 64-bit integer as a uint64.
|
||||
func (r *Rand) Uint64() uint64 { return r.src.Uint64() }
|
||||
|
||||
// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
|
||||
func (r *Rand) Int63() int64 { return int64(r.src.Uint64() &^ (1 << 63)) }
|
||||
|
||||
// Uint32 returns a pseudo-random 32-bit value as a uint32.
|
||||
func (r *Rand) Uint32() uint32 { return uint32(r.Uint64() >> 32) }
|
||||
|
||||
// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
|
||||
func (r *Rand) Int31() int32 { return int32(r.Uint64() >> 33) }
|
||||
|
||||
// Int returns a non-negative pseudo-random int.
|
||||
func (r *Rand) Int() int {
|
||||
u := uint(r.Uint64())
|
||||
return int(u << 1 >> 1) // clear sign bit.
|
||||
}
|
||||
|
||||
const maxUint64 = (1 << 64) - 1
|
||||
|
||||
// Uint64n returns, as a uint64, a pseudo-random number in [0,n).
|
||||
// It is guaranteed more uniform than taking a Source value mod n
|
||||
// for any n that is not a power of 2.
|
||||
func (r *Rand) Uint64n(n uint64) uint64 {
|
||||
if n&(n-1) == 0 { // n is power of two, can mask
|
||||
if n == 0 {
|
||||
panic("invalid argument to Uint64n")
|
||||
}
|
||||
return r.Uint64() & (n - 1)
|
||||
}
|
||||
// If n does not divide v, to avoid bias we must not use
|
||||
// a v that is within maxUint64%n of the top of the range.
|
||||
v := r.Uint64()
|
||||
if v > maxUint64-n { // Fast check.
|
||||
ceiling := maxUint64 - maxUint64%n
|
||||
for v >= ceiling {
|
||||
v = r.Uint64()
|
||||
}
|
||||
}
|
||||
|
||||
return v % n
|
||||
}
|
||||
|
||||
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
|
||||
// It panics if n <= 0.
|
||||
func (r *Rand) Int63n(n int64) int64 {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Int63n")
|
||||
}
|
||||
return int64(r.Uint64n(uint64(n)))
|
||||
}
|
||||
|
||||
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
|
||||
// It panics if n <= 0.
|
||||
func (r *Rand) Int31n(n int32) int32 {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Int31n")
|
||||
}
|
||||
// TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines.
|
||||
return int32(r.Uint64n(uint64(n)))
|
||||
}
|
||||
|
||||
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
|
||||
// It panics if n <= 0.
|
||||
func (r *Rand) Intn(n int) int {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Intn")
|
||||
}
|
||||
// TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines.
|
||||
return int(r.Uint64n(uint64(n)))
|
||||
}
|
||||
|
||||
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
|
||||
func (r *Rand) Float64() float64 {
|
||||
// There is one bug in the value stream: r.Int63() may be so close
|
||||
// to 1<<63 that the division rounds up to 1.0, and we've guaranteed
|
||||
// that the result is always less than 1.0.
|
||||
//
|
||||
// We tried to fix this by mapping 1.0 back to 0.0, but since float64
|
||||
// values near 0 are much denser than near 1, mapping 1 to 0 caused
|
||||
// a theoretically significant overshoot in the probability of returning 0.
|
||||
// Instead of that, if we round up to 1, just try again.
|
||||
// Getting 1 only happens 1/2⁵³ of the time, so most clients
|
||||
// will not observe it anyway.
|
||||
again:
|
||||
f := float64(r.Uint64n(1<<53)) / (1 << 53)
|
||||
if f == 1.0 {
|
||||
goto again // resample; this branch is taken O(never)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
|
||||
func (r *Rand) Float32() float32 {
|
||||
// We do not want to return 1.0.
|
||||
// This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64).
|
||||
again:
|
||||
f := float32(r.Float64())
|
||||
if f == 1 {
|
||||
goto again // resample; this branch is taken O(very rarely)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
|
||||
func (r *Rand) Perm(n int) []int {
|
||||
m := make([]int, n)
|
||||
// In the following loop, the iteration when i=0 always swaps m[0] with m[0].
|
||||
// A change to remove this useless iteration is to assign 1 to i in the init
|
||||
// statement. But Perm also effects r. Making this change will affect
|
||||
// the final state of r. So this change can't be made for compatibility
|
||||
// reasons for Go 1.
|
||||
for i := 0; i < n; i++ {
|
||||
j := r.Intn(i + 1)
|
||||
m[i] = m[j]
|
||||
m[j] = i
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Shuffle pseudo-randomizes the order of elements.
|
||||
// n is the number of elements. Shuffle panics if n < 0.
|
||||
// swap swaps the elements with indexes i and j.
|
||||
func (r *Rand) Shuffle(n int, swap func(i, j int)) {
|
||||
if n < 0 {
|
||||
panic("invalid argument to Shuffle")
|
||||
}
|
||||
|
||||
// Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
|
||||
// Shuffle really ought not be called with n that doesn't fit in 32 bits.
|
||||
// Not only will it take a very long time, but with 2³¹! possible permutations,
|
||||
// there's no way that any PRNG can have a big enough internal state to
|
||||
// generate even a minuscule percentage of the possible permutations.
|
||||
// Nevertheless, the right API signature accepts an int n, so handle it as best we can.
|
||||
i := n - 1
|
||||
for ; i > 1<<31-1-1; i-- {
|
||||
j := int(r.Int63n(int64(i + 1)))
|
||||
swap(i, j)
|
||||
}
|
||||
for ; i > 0; i-- {
|
||||
j := int(r.Int31n(int32(i + 1)))
|
||||
swap(i, j)
|
||||
}
|
||||
}
|
||||
|
||||
// Read generates len(p) random bytes and writes them into p. It
|
||||
// always returns len(p) and a nil error.
|
||||
// Read should not be called concurrently with any other Rand method unless
|
||||
// the underlying source is a LockedSource.
|
||||
func (r *Rand) Read(p []byte) (n int, err error) {
|
||||
if lk, ok := r.src.(*LockedSource); ok {
|
||||
return lk.Read(p, &r.readVal, &r.readPos)
|
||||
}
|
||||
return read(p, r.src, &r.readVal, &r.readPos)
|
||||
}
|
||||
|
||||
func read(p []byte, src Source, readVal *uint64, readPos *int8) (n int, err error) {
|
||||
pos := *readPos
|
||||
val := *readVal
|
||||
rng, _ := src.(*PCGSource)
|
||||
for n = 0; n < len(p); n++ {
|
||||
if pos == 0 {
|
||||
if rng != nil {
|
||||
val = rng.Uint64()
|
||||
} else {
|
||||
val = src.Uint64()
|
||||
}
|
||||
pos = 8
|
||||
}
|
||||
p[n] = byte(val)
|
||||
val >>= 8
|
||||
pos--
|
||||
}
|
||||
*readPos = pos
|
||||
*readVal = val
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Top-level convenience functions
|
||||
*/
|
||||
|
||||
var globalRand = New(&LockedSource{src: *NewSource(1).(*PCGSource)})
|
||||
|
||||
// Type assert that globalRand's source is a LockedSource whose src is a PCGSource.
|
||||
var _ PCGSource = globalRand.src.(*LockedSource).src
|
||||
|
||||
// Seed uses the provided seed value to initialize the default Source to a
|
||||
// deterministic state. If Seed is not called, the generator behaves as
|
||||
// if seeded by Seed(1).
|
||||
// Seed, unlike the Rand.Seed method, is safe for concurrent use.
|
||||
func Seed(seed uint64) { globalRand.Seed(seed) }
|
||||
|
||||
// Int63 returns a non-negative pseudo-random 63-bit integer as an int64
|
||||
// from the default Source.
|
||||
func Int63() int64 { return globalRand.Int63() }
|
||||
|
||||
// Uint32 returns a pseudo-random 32-bit value as a uint32
|
||||
// from the default Source.
|
||||
func Uint32() uint32 { return globalRand.Uint32() }
|
||||
|
||||
// Uint64 returns a pseudo-random 64-bit value as a uint64
|
||||
// from the default Source.
|
||||
func Uint64() uint64 { return globalRand.Uint64() }
|
||||
|
||||
// Int31 returns a non-negative pseudo-random 31-bit integer as an int32
|
||||
// from the default Source.
|
||||
func Int31() int32 { return globalRand.Int31() }
|
||||
|
||||
// Int returns a non-negative pseudo-random int from the default Source.
|
||||
func Int() int { return globalRand.Int() }
|
||||
|
||||
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)
|
||||
// from the default Source.
|
||||
// It panics if n <= 0.
|
||||
func Int63n(n int64) int64 { return globalRand.Int63n(n) }
|
||||
|
||||
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)
|
||||
// from the default Source.
|
||||
// It panics if n <= 0.
|
||||
func Int31n(n int32) int32 { return globalRand.Int31n(n) }
|
||||
|
||||
// Intn returns, as an int, a non-negative pseudo-random number in [0,n)
|
||||
// from the default Source.
|
||||
// It panics if n <= 0.
|
||||
func Intn(n int) int { return globalRand.Intn(n) }
|
||||
|
||||
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)
|
||||
// from the default Source.
|
||||
func Float64() float64 { return globalRand.Float64() }
|
||||
|
||||
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0)
|
||||
// from the default Source.
|
||||
func Float32() float32 { return globalRand.Float32() }
|
||||
|
||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)
|
||||
// from the default Source.
|
||||
func Perm(n int) []int { return globalRand.Perm(n) }
|
||||
|
||||
// Shuffle pseudo-randomizes the order of elements using the default Source.
|
||||
// n is the number of elements. Shuffle panics if n < 0.
|
||||
// swap swaps the elements with indexes i and j.
|
||||
func Shuffle(n int, swap func(i, j int)) { globalRand.Shuffle(n, swap) }
|
||||
|
||||
// Read generates len(p) random bytes from the default Source and
|
||||
// writes them into p. It always returns len(p) and a nil error.
|
||||
// Read, unlike the Rand.Read method, is safe for concurrent use.
|
||||
func Read(p []byte) (n int, err error) { return globalRand.Read(p) }
|
||||
|
||||
// NormFloat64 returns a normally distributed float64 in the range
|
||||
// [-math.MaxFloat64, +math.MaxFloat64] with
|
||||
// standard normal distribution (mean = 0, stddev = 1)
|
||||
// from the default Source.
|
||||
// To produce a different normal distribution, callers can
|
||||
// adjust the output using:
|
||||
//
|
||||
// sample = NormFloat64() * desiredStdDev + desiredMean
|
||||
func NormFloat64() float64 { return globalRand.NormFloat64() }
|
||||
|
||||
// ExpFloat64 returns an exponentially distributed float64 in the range
|
||||
// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter
|
||||
// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source.
|
||||
// To produce a distribution with a different rate parameter,
|
||||
// callers can adjust the output using:
|
||||
//
|
||||
// sample = ExpFloat64() / desiredRateParameter
|
||||
func ExpFloat64() float64 { return globalRand.ExpFloat64() }
|
||||
|
||||
// LockedSource is an implementation of Source that is concurrency-safe.
|
||||
// A Rand using a LockedSource is safe for concurrent use.
|
||||
//
|
||||
// The zero value of LockedSource is valid, but should be seeded before use.
|
||||
type LockedSource struct {
|
||||
lk sync.Mutex
|
||||
src PCGSource
|
||||
}
|
||||
|
||||
func (s *LockedSource) Uint64() (n uint64) {
|
||||
s.lk.Lock()
|
||||
n = s.src.Uint64()
|
||||
s.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LockedSource) Seed(seed uint64) {
|
||||
s.lk.Lock()
|
||||
s.src.Seed(seed)
|
||||
s.lk.Unlock()
|
||||
}
|
||||
|
||||
// seedPos implements Seed for a LockedSource without a race condiiton.
|
||||
func (s *LockedSource) seedPos(seed uint64, readPos *int8) {
|
||||
s.lk.Lock()
|
||||
s.src.Seed(seed)
|
||||
*readPos = 0
|
||||
s.lk.Unlock()
|
||||
}
|
||||
|
||||
// Read implements Read for a LockedSource.
|
||||
func (s *LockedSource) Read(p []byte, readVal *uint64, readPos *int8) (n int, err error) {
|
||||
s.lk.Lock()
|
||||
n, err = read(p, &s.src, readVal, readPos)
|
||||
s.lk.Unlock()
|
||||
return
|
||||
}
|
93
mongo/internal/randutil/rand/rng.go
Normal file
93
mongo/internal/randutil/rand/rng.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/rng.go
|
||||
|
||||
// Copyright 2017 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 rand
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// PCGSource is an implementation of a 64-bit permuted congruential
|
||||
// generator as defined in
|
||||
//
|
||||
// PCG: A Family of Simple Fast Space-Efficient Statistically Good
|
||||
// Algorithms for Random Number Generation
|
||||
// Melissa E. O’Neill, Harvey Mudd College
|
||||
// http://www.pcg-random.org/pdf/toms-oneill-pcg-family-v1.02.pdf
|
||||
//
|
||||
// The generator here is the congruential generator PCG XSL RR 128/64 (LCG)
|
||||
// as found in the software available at http://www.pcg-random.org/.
|
||||
// It has period 2^128 with 128 bits of state, producing 64-bit values.
|
||||
// Is state is represented by two uint64 words.
|
||||
type PCGSource struct {
|
||||
low uint64
|
||||
high uint64
|
||||
}
|
||||
|
||||
const (
|
||||
maxUint32 = (1 << 32) - 1
|
||||
|
||||
multiplier = 47026247687942121848144207491837523525
|
||||
mulHigh = multiplier >> 64
|
||||
mulLow = multiplier & maxUint64
|
||||
|
||||
increment = 117397592171526113268558934119004209487
|
||||
incHigh = increment >> 64
|
||||
incLow = increment & maxUint64
|
||||
|
||||
// TODO: Use these?
|
||||
initializer = 245720598905631564143578724636268694099
|
||||
initHigh = initializer >> 64
|
||||
initLow = initializer & maxUint64
|
||||
)
|
||||
|
||||
// Seed uses the provided seed value to initialize the generator to a deterministic state.
|
||||
func (pcg *PCGSource) Seed(seed uint64) {
|
||||
pcg.low = seed
|
||||
pcg.high = seed // TODO: What is right?
|
||||
}
|
||||
|
||||
// Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.
|
||||
func (pcg *PCGSource) Uint64() uint64 {
|
||||
pcg.multiply()
|
||||
pcg.add()
|
||||
// XOR high and low 64 bits together and rotate right by high 6 bits of state.
|
||||
return bits.RotateLeft64(pcg.high^pcg.low, -int(pcg.high>>58))
|
||||
}
|
||||
|
||||
func (pcg *PCGSource) add() {
|
||||
var carry uint64
|
||||
pcg.low, carry = Add64(pcg.low, incLow, 0)
|
||||
pcg.high, _ = Add64(pcg.high, incHigh, carry)
|
||||
}
|
||||
|
||||
func (pcg *PCGSource) multiply() {
|
||||
hi, lo := Mul64(pcg.low, mulLow)
|
||||
hi += pcg.high * mulLow
|
||||
hi += pcg.low * mulHigh
|
||||
pcg.low = lo
|
||||
pcg.high = hi
|
||||
}
|
||||
|
||||
// MarshalBinary returns the binary representation of the current state of the generator.
|
||||
func (pcg *PCGSource) MarshalBinary() ([]byte, error) {
|
||||
var buf [16]byte
|
||||
binary.BigEndian.PutUint64(buf[:8], pcg.high)
|
||||
binary.BigEndian.PutUint64(buf[8:], pcg.low)
|
||||
return buf[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary sets the state of the generator to the state represented in data.
|
||||
func (pcg *PCGSource) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 16 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
pcg.low = binary.BigEndian.Uint64(data[8:])
|
||||
pcg.high = binary.BigEndian.Uint64(data[:8])
|
||||
return nil
|
||||
}
|
39
mongo/internal/randutil/randutil.go
Normal file
39
mongo/internal/randutil/randutil.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// Package randutil provides common random number utilities.
|
||||
package randutil
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
xrand "go.mongodb.org/mongo-driver/internal/randutil/rand"
|
||||
)
|
||||
|
||||
// NewLockedRand returns a new "x/exp/rand" pseudo-random number generator seeded with a
|
||||
// cryptographically-secure random number.
|
||||
// It is safe to use from multiple goroutines.
|
||||
func NewLockedRand() *xrand.Rand {
|
||||
var randSrc = new(xrand.LockedSource)
|
||||
randSrc.Seed(cryptoSeed())
|
||||
return xrand.New(randSrc)
|
||||
}
|
||||
|
||||
// cryptoSeed returns a random uint64 read from the "crypto/rand" random number generator. It is
|
||||
// intended to be used to seed pseudorandom number generators at package initialization. It panics
|
||||
// if it encounters any errors.
|
||||
func cryptoSeed() uint64 {
|
||||
var b [8]byte
|
||||
_, err := io.ReadFull(crand.Reader, b[:])
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to read 8 bytes from a \"crypto/rand\".Reader: %v", err))
|
||||
}
|
||||
|
||||
return (uint64(b[0]) << 0) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) |
|
||||
(uint64(b[4]) << 32) | (uint64(b[5]) << 40) | (uint64(b[6]) << 48) | (uint64(b[7]) << 56)
|
||||
}
|
22
mongo/internal/randutil/randutil_test.go
Normal file
22
mongo/internal/randutil/randutil_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package randutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCryptoSeed(t *testing.T) {
|
||||
seeds := make(map[uint64]bool)
|
||||
for i := 1; i < 1000000; i++ {
|
||||
s := cryptoSeed()
|
||||
require.False(t, seeds[s], "cryptoSeed returned a duplicate value %d", s)
|
||||
seeds[s] = true
|
||||
}
|
||||
}
|
45
mongo/internal/string_util.go
Normal file
45
mongo/internal/string_util.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// StringSliceFromRawElement decodes the provided BSON element into a []string. This internally calls
|
||||
// StringSliceFromRawValue on the element's value. The error conditions outlined in that function's documentation
|
||||
// apply for this function as well.
|
||||
func StringSliceFromRawElement(element bson.RawElement) ([]string, error) {
|
||||
return StringSliceFromRawValue(element.Key(), element.Value())
|
||||
}
|
||||
|
||||
// StringSliceFromRawValue decodes the provided BSON value into a []string. This function returns an error if the value
|
||||
// is not an array or any of the elements in the array are not strings. The name parameter is used to add context to
|
||||
// error messages.
|
||||
func StringSliceFromRawValue(name string, val bson.RawValue) ([]string, error) {
|
||||
arr, ok := val.ArrayOK()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected '%s' to be an array but it's a BSON %s", name, val.Type)
|
||||
}
|
||||
|
||||
arrayValues, err := arr.Values()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
strs := make([]string, 0, len(arrayValues))
|
||||
for _, arrayVal := range arrayValues {
|
||||
str, ok := arrayVal.StringValueOK()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected '%s' to be an array of strings, but found a BSON %s", name, arrayVal.Type)
|
||||
}
|
||||
strs = append(strs, str)
|
||||
}
|
||||
return strs, nil
|
||||
}
|
7
mongo/internal/test/compilecheck/go.mod
Normal file
7
mongo/internal/test/compilecheck/go.mod
Normal file
@@ -0,0 +1,7 @@
|
||||
module go.mongodb.go/mongo-driver/internal/test/compilecheck
|
||||
|
||||
go 1.13
|
||||
|
||||
replace go.mongodb.org/mongo-driver => ../../../
|
||||
|
||||
require go.mongodb.org/mongo-driver v1.11.6
|
54
mongo/internal/test/compilecheck/go.sum
Normal file
54
mongo/internal/test/compilecheck/go.sum
Normal file
@@ -0,0 +1,54 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
21
mongo/internal/test/compilecheck/main.go
Normal file
21
mongo/internal/test/compilecheck/main.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) MongoDB, Inc. 2023-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_, _ = mongo.Connect(context.Background(), options.Client())
|
||||
fmt.Println(bson.D{{Key: "key", Value: "value"}})
|
||||
}
|
137
mongo/internal/testutil/assert/assert.go
Normal file
137
mongo/internal/testutil/assert/assert.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package assert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
var cmpOpts sync.Map
|
||||
var errorCompareFn = func(e1, e2 error) bool {
|
||||
if e1 == nil || e2 == nil {
|
||||
return e1 == nil && e2 == nil
|
||||
}
|
||||
|
||||
return e1.Error() == e2.Error()
|
||||
}
|
||||
var errorCompareOpts = cmp.Options{cmp.Comparer(errorCompareFn)}
|
||||
|
||||
// RegisterOpts registers go-cmp options for a type. These options will be used when comparing two objects for equality.
|
||||
func RegisterOpts(t reflect.Type, opts ...cmp.Option) {
|
||||
cmpOpts.Store(t, cmp.Options(opts))
|
||||
}
|
||||
|
||||
// Equal compares first and second for equality. The objects must be of the same type.
|
||||
// If the objects are not equal, the test will be failed with an error message containing msg and args.
|
||||
func Equal(t testing.TB, first, second interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
if !cmp.Equal(first, second, getCmpOpts(first)...) {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// NotEqual compares first and second for inequality. The objects must be of the same type.
|
||||
func NotEqual(t testing.TB, first, second interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
if cmp.Equal(first, second, getCmpOpts(first)...) {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// True asserts that the obj parameter is a boolean with value true.
|
||||
func True(t testing.TB, obj interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
b, ok := obj.(bool)
|
||||
if !ok || !b {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// False asserts that the obj parameter is a boolean with value false.
|
||||
func False(t testing.TB, obj interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
b, ok := obj.(bool)
|
||||
if !ok || b {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Nil asserts that the obj parameter is nil.
|
||||
func Nil(t testing.TB, obj interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
if !isNil(obj) {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// NotNil asserts that the obj parameter is not nil.
|
||||
func NotNil(t testing.TB, obj interface{}, msg string, args ...interface{}) {
|
||||
t.Helper()
|
||||
if isNil(obj) {
|
||||
t.Fatalf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Soon runs the provided callback for a maximum of timeoutMS milliseconds. The provided callback
|
||||
// should respect the passed-in context and cease execution when it has expired.
|
||||
func Soon(t testing.TB, callback func(ctx context.Context), timeout time.Duration) {
|
||||
t.Helper()
|
||||
|
||||
// Create context to manually cancel callback after Soon assertion.
|
||||
callbackCtx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
done := make(chan struct{})
|
||||
fullCallback := func() {
|
||||
callback(callbackCtx)
|
||||
done <- struct{}{}
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
go fullCallback()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-timer.C:
|
||||
t.Fatalf("timed out in %s waiting for callback", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func getCmpOpts(obj interface{}) cmp.Options {
|
||||
opts, ok := cmpOpts.Load(reflect.TypeOf(obj))
|
||||
if ok {
|
||||
return opts.(cmp.Options)
|
||||
}
|
||||
|
||||
if _, ok := obj.(error); ok {
|
||||
return errorCompareOpts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isNil(object interface{}) bool {
|
||||
if object == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(object)
|
||||
switch val.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return val.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
275
mongo/internal/testutil/config.go
Normal file
275
mongo/internal/testutil/config.go
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/event"
|
||||
"go.mongodb.org/mongo-driver/mongo/description"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
|
||||
)
|
||||
|
||||
var connectionString connstring.ConnString
|
||||
var connectionStringOnce sync.Once
|
||||
var connectionStringErr error
|
||||
var liveTopology *topology.Topology
|
||||
var liveTopologyOnce sync.Once
|
||||
var liveTopologyErr error
|
||||
|
||||
// AddOptionsToURI appends connection string options to a URI.
|
||||
func AddOptionsToURI(uri string, opts ...string) string {
|
||||
if !strings.ContainsRune(uri, '?') {
|
||||
if uri[len(uri)-1] != '/' {
|
||||
uri += "/"
|
||||
}
|
||||
|
||||
uri += "?"
|
||||
} else {
|
||||
uri += "&"
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
uri += opt
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
// AddTLSConfigToURI checks for the environmental variable indicating that the tests are being run
|
||||
// on an SSL-enabled server, and if so, returns a new URI with the necessary configuration.
|
||||
func AddTLSConfigToURI(uri string) string {
|
||||
caFile := os.Getenv("MONGO_GO_DRIVER_CA_FILE")
|
||||
if len(caFile) == 0 {
|
||||
return uri
|
||||
}
|
||||
|
||||
return AddOptionsToURI(uri, "ssl=true&sslCertificateAuthorityFile=", caFile)
|
||||
}
|
||||
|
||||
// AddCompressorToURI checks for the environment variable indicating that the tests are being run with compression
|
||||
// enabled. If so, it returns a new URI with the necessary configuration
|
||||
func AddCompressorToURI(uri string) string {
|
||||
comp := os.Getenv("MONGO_GO_DRIVER_COMPRESSOR")
|
||||
if len(comp) == 0 {
|
||||
return uri
|
||||
}
|
||||
|
||||
return AddOptionsToURI(uri, "compressors=", comp)
|
||||
}
|
||||
|
||||
// MonitoredTopology returns a new topology with the command monitor attached
|
||||
func MonitoredTopology(t *testing.T, dbName string, monitor *event.CommandMonitor) *topology.Topology {
|
||||
uri, err := MongoDBURI()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := topology.NewConfig(options.Client().ApplyURI(uri).SetMonitor(monitor), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
monitoredTopology, err := topology.New(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
_ = monitoredTopology.Connect()
|
||||
|
||||
err = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, "dropDatabase", 1))).
|
||||
Database(dbName).ServerSelector(description.WriteSelector()).Deployment(monitoredTopology).Execute(context.Background())
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return monitoredTopology
|
||||
}
|
||||
|
||||
// Topology gets the globally configured topology.
|
||||
func Topology(t *testing.T) *topology.Topology {
|
||||
uri, err := MongoDBURI()
|
||||
require.NoError(t, err, "error constructing mongodb URI: %v", err)
|
||||
cfg, err := topology.NewConfig(options.Client().ApplyURI(uri), nil)
|
||||
require.NoError(t, err, "error constructing topology config: %v", err)
|
||||
|
||||
liveTopologyOnce.Do(func() {
|
||||
var err error
|
||||
liveTopology, err = topology.New(cfg)
|
||||
if err != nil {
|
||||
liveTopologyErr = err
|
||||
} else {
|
||||
_ = liveTopology.Connect()
|
||||
|
||||
err = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, "dropDatabase", 1))).
|
||||
Database(DBName(t)).ServerSelector(description.WriteSelector()).Deployment(liveTopology).Execute(context.Background())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
if liveTopologyErr != nil {
|
||||
t.Fatal(liveTopologyErr)
|
||||
}
|
||||
|
||||
return liveTopology
|
||||
}
|
||||
|
||||
// TopologyWithCredential takes an "options.Credential" object and returns a connected topology.
|
||||
func TopologyWithCredential(t *testing.T, credential options.Credential) *topology.Topology {
|
||||
uri, err := MongoDBURI()
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing mongodb URI: %v", err)
|
||||
}
|
||||
cfg, err := topology.NewConfig(options.Client().ApplyURI(uri).SetAuth(credential), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing topology config: %v", err)
|
||||
}
|
||||
topology, err := topology.New(cfg)
|
||||
if err != nil {
|
||||
t.Fatal("Could not construct topology")
|
||||
}
|
||||
err = topology.Connect()
|
||||
if err != nil {
|
||||
t.Fatal("Could not start topology connection")
|
||||
}
|
||||
return topology
|
||||
}
|
||||
|
||||
// ColName gets a collection name that should be unique
|
||||
// to the currently executing test.
|
||||
func ColName(t *testing.T) string {
|
||||
// Get this indirectly to avoid copying a mutex
|
||||
v := reflect.Indirect(reflect.ValueOf(t))
|
||||
name := v.FieldByName("name")
|
||||
return name.String()
|
||||
}
|
||||
|
||||
// MongoDBURI will construct the MongoDB URI from the MONGODB_URI environment variable for testing. The default host is
|
||||
// "localhost" and the default port is "27017"
|
||||
func MongoDBURI() (string, error) {
|
||||
uri := os.Getenv("MONGODB_URI")
|
||||
if uri == "" {
|
||||
uri = "mongodb://localhost:27017"
|
||||
}
|
||||
|
||||
uri = AddTLSConfigToURI(uri)
|
||||
uri = AddCompressorToURI(uri)
|
||||
uri, err := AddServerlessAuthCredentials(uri)
|
||||
return uri, err
|
||||
}
|
||||
|
||||
// AddServerlessAuthCredentials will attempt to construct the serverless auth credentials for a URI.
|
||||
func AddServerlessAuthCredentials(uri string) (string, error) {
|
||||
if os.Getenv("SERVERLESS") != "serverless" {
|
||||
return uri, nil
|
||||
}
|
||||
user := os.Getenv("SERVERLESS_ATLAS_USER")
|
||||
if user == "" {
|
||||
return "", fmt.Errorf("serverless expects SERVERLESS_ATLAS_USER to be set")
|
||||
}
|
||||
password := os.Getenv("SERVERLESS_ATLAS_PASSWORD")
|
||||
if password == "" {
|
||||
return "", fmt.Errorf("serverless expects SERVERLESS_ATLAS_PASSWORD to be set")
|
||||
}
|
||||
|
||||
var scheme string
|
||||
// remove the scheme
|
||||
if strings.HasPrefix(uri, "mongodb+srv://") {
|
||||
scheme = "mongodb+srv://"
|
||||
} else if strings.HasPrefix(uri, "mongodb://") {
|
||||
scheme = "mongodb://"
|
||||
} else {
|
||||
return "", fmt.Errorf("scheme must be \"mongodb\" or \"mongodb+srv\"")
|
||||
}
|
||||
|
||||
uri = scheme + user + ":" + password + "@" + uri[len(scheme):]
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
// ConnString gets the globally configured connection string.
|
||||
func ConnString(t *testing.T) connstring.ConnString {
|
||||
connectionStringOnce.Do(func() {
|
||||
uri, err := MongoDBURI()
|
||||
require.NoError(t, err, "error constructing mongodb URI: %v", err)
|
||||
|
||||
connectionString, err = connstring.ParseAndValidate(uri)
|
||||
if err != nil {
|
||||
connectionStringErr = err
|
||||
}
|
||||
})
|
||||
if connectionStringErr != nil {
|
||||
t.Fatal(connectionStringErr)
|
||||
}
|
||||
|
||||
return connectionString
|
||||
}
|
||||
|
||||
func GetConnString() (connstring.ConnString, error) {
|
||||
mongodbURI := os.Getenv("MONGODB_URI")
|
||||
if mongodbURI == "" {
|
||||
mongodbURI = "mongodb://localhost:27017"
|
||||
}
|
||||
|
||||
mongodbURI = AddTLSConfigToURI(mongodbURI)
|
||||
|
||||
cs, err := connstring.ParseAndValidate(mongodbURI)
|
||||
if err != nil {
|
||||
return connstring.ConnString{}, err
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// DBName gets the globally configured database name.
|
||||
func DBName(t *testing.T) string {
|
||||
return GetDBName(ConnString(t))
|
||||
}
|
||||
|
||||
func GetDBName(cs connstring.ConnString) string {
|
||||
if cs.Database != "" {
|
||||
return cs.Database
|
||||
}
|
||||
|
||||
return fmt.Sprintf("mongo-go-driver-%d", os.Getpid())
|
||||
}
|
||||
|
||||
// CompareVersions compares two version number strings (i.e. positive integers separated by
|
||||
// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is
|
||||
// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.
|
||||
//
|
||||
// Returns a positive int if version1 is greater than version2, a negative int if version1 is less
|
||||
// than version2, and 0 if version1 is equal to version2.
|
||||
func CompareVersions(t *testing.T, v1 string, v2 string) int {
|
||||
n1 := strings.Split(v1, ".")
|
||||
n2 := strings.Split(v2, ".")
|
||||
|
||||
for i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {
|
||||
i1, err := strconv.Atoi(n1[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
i2, err := strconv.Atoi(n2[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
difference := i1 - i2
|
||||
if difference != 0 {
|
||||
return difference
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
61
mongo/internal/testutil/helpers/helpers.go
Normal file
61
mongo/internal/testutil/helpers/helpers.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// FindJSONFilesInDir finds the JSON files in a directory.
|
||||
func FindJSONFilesInDir(t *testing.T, dir string) []string {
|
||||
t.Helper()
|
||||
|
||||
files := make([]string, 0)
|
||||
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || path.Ext(entry.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
|
||||
files = append(files, entry.Name())
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
// RawToDocuments converts a bson.Raw that is internally an array of documents to []bson.Raw.
|
||||
func RawToDocuments(doc bson.Raw) []bson.Raw {
|
||||
values, err := doc.Values()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error converting BSON document to values: %v", err))
|
||||
}
|
||||
|
||||
out := make([]bson.Raw, len(values))
|
||||
for i := range values {
|
||||
out[i] = values[i].Document()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// RawToInterfaces takes one or many bson.Raw documents and returns them as a []interface{}.
|
||||
func RawToInterfaces(docs ...bson.Raw) []interface{} {
|
||||
out := make([]interface{}, len(docs))
|
||||
for i := range docs {
|
||||
out[i] = docs[i]
|
||||
}
|
||||
return out
|
||||
}
|
14
mongo/internal/testutil/israce/norace.go
Normal file
14
mongo/internal/testutil/israce/norace.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
// Package israce reports if the Go race detector is enabled.
|
||||
package israce
|
||||
|
||||
// Enabled reports if the race detector is enabled.
|
||||
const Enabled = false
|
14
mongo/internal/testutil/israce/race.go
Normal file
14
mongo/internal/testutil/israce/race.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
//go:build race
|
||||
// +build race
|
||||
|
||||
// Package israce reports if the Go race detector is enabled.
|
||||
package israce
|
||||
|
||||
// Enabled reports if the race detector is enabled.
|
||||
const Enabled = true
|
78
mongo/internal/testutil/monitor/monitor.go
Normal file
78
mongo/internal/testutil/monitor/monitor.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// Package monitor provides test types that are used to monitor client state and actions via the
|
||||
// various monitor types supported by the driver.
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.mongodb.org/mongo-driver/event"
|
||||
)
|
||||
|
||||
// TestPoolMonitor exposes an *event.TestPoolMonitor and collects all events logged to that
|
||||
// *event.TestPoolMonitor. It is safe to use from multiple concurrent goroutines.
|
||||
type TestPoolMonitor struct {
|
||||
*event.PoolMonitor
|
||||
|
||||
events []*event.PoolEvent
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewTestPoolMonitor() *TestPoolMonitor {
|
||||
tpm := &TestPoolMonitor{
|
||||
events: make([]*event.PoolEvent, 0),
|
||||
}
|
||||
tpm.PoolMonitor = &event.PoolMonitor{
|
||||
Event: func(evt *event.PoolEvent) {
|
||||
tpm.mu.Lock()
|
||||
defer tpm.mu.Unlock()
|
||||
tpm.events = append(tpm.events, evt)
|
||||
},
|
||||
}
|
||||
return tpm
|
||||
}
|
||||
|
||||
// Events returns a copy of the events collected by the testPoolMonitor. Filters can optionally be
|
||||
// applied to the returned events set and are applied using AND logic (i.e. all filters must return
|
||||
// true to include the event in the result).
|
||||
func (tpm *TestPoolMonitor) Events(filters ...func(*event.PoolEvent) bool) []*event.PoolEvent {
|
||||
tpm.mu.RLock()
|
||||
defer tpm.mu.RUnlock()
|
||||
|
||||
filtered := make([]*event.PoolEvent, 0, len(tpm.events))
|
||||
for _, evt := range tpm.events {
|
||||
keep := true
|
||||
for _, filter := range filters {
|
||||
if !filter(evt) {
|
||||
keep = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if keep {
|
||||
filtered = append(filtered, evt)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// ClearEvents will reset the events collected by the testPoolMonitor.
|
||||
func (tpm *TestPoolMonitor) ClearEvents() {
|
||||
tpm.mu.Lock()
|
||||
defer tpm.mu.Unlock()
|
||||
tpm.events = tpm.events[:0]
|
||||
}
|
||||
|
||||
// IsPoolCleared returns true if there are any events of type "event.PoolCleared" in the events
|
||||
// recorded by the testPoolMonitor.
|
||||
func (tpm *TestPoolMonitor) IsPoolCleared() bool {
|
||||
poolClearedEvents := tpm.Events(func(evt *event.PoolEvent) bool {
|
||||
return evt.Type == event.PoolCleared
|
||||
})
|
||||
return len(poolClearedEvents) > 0
|
||||
}
|
59
mongo/internal/testutil/ops.go
Normal file
59
mongo/internal/testutil/ops.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package testutil // import "go.mongodb.org/mongo-driver/internal/testutil"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/mongo/description"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/mongo/writeconcern"
|
||||
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver"
|
||||
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
|
||||
)
|
||||
|
||||
// DropCollection drops the collection in the test cluster.
|
||||
func DropCollection(t *testing.T, dbname, colname string) {
|
||||
err := operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendStringElement(nil, "drop", colname))).
|
||||
Database(dbname).ServerSelector(description.WriteSelector()).Deployment(Topology(t)).
|
||||
Execute(context.Background())
|
||||
if de, ok := err.(driver.Error); err != nil && !(ok && de.NamespaceNotFound()) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// AutoInsertDocs inserts the docs into the test cluster.
|
||||
func AutoInsertDocs(t *testing.T, writeConcern *writeconcern.WriteConcern, docs ...bsoncore.Document) {
|
||||
InsertDocs(t, DBName(t), ColName(t), writeConcern, docs...)
|
||||
}
|
||||
|
||||
// InsertDocs inserts the docs into the test cluster.
|
||||
func InsertDocs(t *testing.T, dbname, colname string, writeConcern *writeconcern.WriteConcern, docs ...bsoncore.Document) {
|
||||
err := operation.NewInsert(docs...).Collection(colname).Database(dbname).
|
||||
Deployment(Topology(t)).ServerSelector(description.WriteSelector()).Execute(context.Background())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// RunCommand runs an arbitrary command on a given database of target server
|
||||
func RunCommand(t *testing.T, s driver.Server, db string, cmd bsoncore.Document) (bsoncore.Document, error) {
|
||||
op := operation.NewCommand(cmd).
|
||||
Database(db).Deployment(driver.SingleServerDeployment{Server: s})
|
||||
err := op.Execute(context.Background())
|
||||
res := op.Result()
|
||||
return res, err
|
||||
}
|
||||
|
||||
// AddTestServerAPIVersion adds the latest server API version in a ServerAPIOptions to passed-in opts.
|
||||
func AddTestServerAPIVersion(opts *options.ClientOptions) {
|
||||
if os.Getenv("REQUIRE_API_VERSION") == "true" {
|
||||
opts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))
|
||||
}
|
||||
}
|
22
mongo/internal/uri_validation_errors.go
Normal file
22
mongo/internal/uri_validation_errors.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrLoadBalancedWithMultipleHosts is returned when loadBalanced=true is specified in a URI with multiple hosts.
|
||||
ErrLoadBalancedWithMultipleHosts = errors.New("loadBalanced cannot be set to true if multiple hosts are specified")
|
||||
// ErrLoadBalancedWithReplicaSet is returned when loadBalanced=true is specified in a URI with the replicaSet option.
|
||||
ErrLoadBalancedWithReplicaSet = errors.New("loadBalanced cannot be set to true if a replica set name is specified")
|
||||
// ErrLoadBalancedWithDirectConnection is returned when loadBalanced=true is specified in a URI with the directConnection option.
|
||||
ErrLoadBalancedWithDirectConnection = errors.New("loadBalanced cannot be set to true if the direct connection option is specified")
|
||||
// ErrSRVMaxHostsWithReplicaSet is returned when srvMaxHosts > 0 is specified in a URI with the replicaSet option.
|
||||
ErrSRVMaxHostsWithReplicaSet = errors.New("srvMaxHosts cannot be a positive value if a replica set name is specified")
|
||||
// ErrSRVMaxHostsWithLoadBalanced is returned when srvMaxHosts > 0 is specified in a URI with loadBalanced=true.
|
||||
ErrSRVMaxHostsWithLoadBalanced = errors.New("srvMaxHosts cannot be a positive value if loadBalanced is set to true")
|
||||
)
|
53
mongo/internal/uuid/uuid.go
Normal file
53
mongo/internal/uuid/uuid.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (C) MongoDB, Inc. 2017-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"go.mongodb.org/mongo-driver/internal/randutil"
|
||||
)
|
||||
|
||||
// UUID represents a UUID.
|
||||
type UUID [16]byte
|
||||
|
||||
// A source is a UUID generator that reads random values from a io.Reader.
|
||||
// It should be safe to use from multiple goroutines.
|
||||
type source struct {
|
||||
random io.Reader
|
||||
}
|
||||
|
||||
// new returns a random UUIDv4 with bytes read from the source's random number generator.
|
||||
func (s *source) new() (UUID, error) {
|
||||
var uuid UUID
|
||||
_, err := io.ReadFull(s.random, uuid[:])
|
||||
if err != nil {
|
||||
return UUID{}, err
|
||||
}
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// newSource returns a source that uses a pseudo-random number generator in reandutil package.
|
||||
// It is intended to be used to initialize the package-global UUID generator.
|
||||
func newSource() *source {
|
||||
return &source{
|
||||
random: randutil.NewLockedRand(),
|
||||
}
|
||||
}
|
||||
|
||||
// globalSource is a package-global pseudo-random UUID generator.
|
||||
var globalSource = newSource()
|
||||
|
||||
// New returns a random UUIDv4. It uses a global pseudo-random number generator in randutil
|
||||
// at package initialization.
|
||||
//
|
||||
// New should not be used to generate cryptographically-secure random UUIDs.
|
||||
func New() (UUID, error) {
|
||||
return globalSource.new()
|
||||
}
|
90
mongo/internal/uuid/uuid_test.go
Normal file
90
mongo/internal/uuid/uuid_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (C) MongoDB, Inc. 2022-present.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// GODRIVER-2349
|
||||
// Test that initializing many package-global UUID sources concurrently never leads to any duplicate
|
||||
// UUIDs being generated.
|
||||
func TestGlobalSource(t *testing.T) {
|
||||
t.Run("exp rand 1 UUID x 1,000,000 goroutines using a global source", func(t *testing.T) {
|
||||
// Read a UUID from each of 1,000,000 goroutines and assert that there is never a duplicate value.
|
||||
const iterations = 1e6
|
||||
uuids := new(sync.Map)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
uuid, err := New()
|
||||
require.NoError(t, err, "new() error")
|
||||
_, ok := uuids.Load(uuid)
|
||||
require.Falsef(t, ok, "New returned a duplicate UUID on iteration %d: %v", i, uuid)
|
||||
uuids.Store(uuid, true)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
t.Run("exp rand 1 UUID x 1,000,000 goroutines each initializing a new source", func(t *testing.T) {
|
||||
// Read a UUID from each of 1,000,000 goroutines and assert that there is never a duplicate value.
|
||||
// The goal is to emulate many separate Go driver processes starting at the same time and
|
||||
// initializing the uuid package at the same time.
|
||||
const iterations = 1e6
|
||||
uuids := new(sync.Map)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
s := newSource()
|
||||
uuid, err := s.new()
|
||||
require.NoError(t, err, "new() error")
|
||||
_, ok := uuids.Load(uuid)
|
||||
require.Falsef(t, ok, "New returned a duplicate UUID on iteration %d: %v", i, uuid)
|
||||
uuids.Store(uuid, true)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
t.Run("exp rand 1,000 UUIDs x 1,000 goroutines each initializing a new source", func(t *testing.T) {
|
||||
// Read 1,000 UUIDs from each goroutine and assert that there is never a duplicate value, either
|
||||
// from the same goroutine or from separate goroutines.
|
||||
const iterations = 1000
|
||||
uuids := new(sync.Map)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
s := newSource()
|
||||
for j := 0; j < iterations; j++ {
|
||||
uuid, err := s.new()
|
||||
require.NoError(t, err, "new() error")
|
||||
_, ok := uuids.Load(uuid)
|
||||
require.Falsef(t, ok, "goroutine %d returned a duplicate UUID on iteration %d: %v", i, j, uuid)
|
||||
uuids.Store(uuid, true)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkUuidGeneration(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user