File: uid_validity_generator.go

package info (click to toggle)
golang-github-protonmail-gluon 0.17.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 16,020 kB
  • sloc: sh: 55; makefile: 5
file content (87 lines) | stat: -rw-r--r-- 2,242 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package imap

import (
	"fmt"
	"sync/atomic"
	"time"
)

type UIDValidityGenerator interface {
	Generate() (UID, error)
}

type EpochUIDValidityGenerator struct {
	epochStart time.Time
	lastUID    uint32
}

func NewEpochUIDValidityGenerator(epochStart time.Time) *EpochUIDValidityGenerator {
	return &EpochUIDValidityGenerator{
		epochStart: epochStart,
	}
}

func DefaultEpochUIDValidityGenerator() *EpochUIDValidityGenerator {
	return NewEpochUIDValidityGenerator(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC))
}

func (e *EpochUIDValidityGenerator) Generate() (UID, error) {
	timeStamp := uint64(time.Now().Sub(e.epochStart).Seconds())
	if timeStamp > uint64(0xFFFFFFFF) {
		return 0, fmt.Errorf("failed to generate uid validity, interval exceeded maximum capacity")
	}

	timeStampU32 := uint32(timeStamp)

	// This loops is here to ensure that two successive calls to Generate that happen during the same second
	// can still generate unique values. To avoid waiting another second until the values are different,
	// we keep bumping the last generated value until it is greater than the last generated value.
	for {
		lastGenerated := atomic.LoadUint32(&e.lastUID)

		// Not enough time elapsed between the last time
		if lastGenerated >= timeStampU32 {
			if timeStampU32 == 0xFFFFFFFF {
				return 0, fmt.Errorf("failed to generate uid validity, interval exceeded maximum capacity")
			}

			timeStampU32 += 1

			continue
		}

		if !atomic.CompareAndSwapUint32(&e.lastUID, lastGenerated, timeStampU32) {
			continue
		}

		return UID(timeStampU32), nil
	}
}

type IncrementalUIDValidityGenerator struct {
	counter uint32
}

func (i *IncrementalUIDValidityGenerator) Generate() (UID, error) {
	return UID(atomic.AddUint32(&i.counter, 1)), nil
}

func (i *IncrementalUIDValidityGenerator) GetValue() UID {
	return UID(atomic.LoadUint32(&i.counter))
}

func NewIncrementalUIDValidityGenerator() *IncrementalUIDValidityGenerator {
	return &IncrementalUIDValidityGenerator{}
}

type FixedUIDValidityGenerator struct {
	Value UID
}

func (f FixedUIDValidityGenerator) Generate() (UID, error) {
	return f.Value, nil
}

func NewFixedUIDValidityGenerator(value UID) *FixedUIDValidityGenerator {
	return &FixedUIDValidityGenerator{Value: value}
}