Skip to content

Commit

Permalink
Merge branch 'master' into ky/fix-must-parse-func
Browse files Browse the repository at this point in the history
  • Loading branch information
bormanp committed Feb 22, 2024
2 parents 489959a + b5b9aeb commit 7e7e384
Show file tree
Hide file tree
Showing 13 changed files with 582 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/apidiff.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
pull_request:
branches:
- master
permissions:
contents: read
jobs:
compat:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
pull_request:
branches:
- master
permissions:
contents: read
jobs:
unit-tests:
strategy:
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,41 @@
# Changelog

## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)


### Features

* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))


### Bug Fixes

* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))

## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)


### Features

* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))

## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)


### Features

* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))

### Fixes

* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)

## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)


### Bug Fixes

* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))

## Changelog
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ please explain why in the pull request description.

### Releasing

Commits that would precipitate a SemVer change, as desrcibed in the Conventional
Commits that would precipitate a SemVer change, as described in the Conventional
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
to create a release candidate pull request. Once submitted, `release-please`
will create a release.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ go get github.com/google/uuid

Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here:
http://pkg.go.dev/github.com/google/uuid
https://pkg.go.dev/github.com/google/uuid
6 changes: 6 additions & 0 deletions hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ var (
NameSpaceOID = MustParse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
NameSpaceX500 = MustParse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
Nil UUID // empty UUID, all zeros

// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
Max = UUID{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
}
)

// NewHash returns a new UUID derived from the hash of space concatenated with
Expand Down
51 changes: 51 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,57 @@ func TestJSON(t *testing.T) {
}
}

func TestJSONUnmarshal(t *testing.T) {
type S struct {
ID1 UUID
ID2 UUID `json:"ID2,omitempty"`
}

testCases := map[string]struct {
data []byte
expectedError error
expectedResult UUID
}{
"success": {
data: []byte(`{"ID1": "f47ac10b-58cc-0372-8567-0e02b2c3d479"}`),
expectedError: nil,
expectedResult: testUUID,
},
"zero": {
data: []byte(`{"ID1": "00000000-0000-0000-0000-000000000000"}`),
expectedError: nil,
expectedResult: Nil,
},
"null": {
data: []byte(`{"ID1": null}`),
expectedError: nil,
expectedResult: Nil,
},
"empty": {
data: []byte(`{"ID1": ""}`),
expectedError: invalidLengthError{len: 0},
expectedResult: Nil,
},
"omitempty": {
data: []byte(`{"ID2": ""}`),
expectedError: invalidLengthError{len: 0},
expectedResult: Nil,
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
var s S
if err := json.Unmarshal(tc.data, &s); err != tc.expectedError {
t.Errorf("unexpected error: got %v, want %v", err, tc.expectedError)
}
if !reflect.DeepEqual(s.ID1, tc.expectedResult) {
t.Errorf("got %#v, want %#v", s.ID1, tc.expectedResult)
}
})
}
}

func BenchmarkUUID_MarshalJSON(b *testing.B) {
x := &struct {
UUID UUID `json:"uuid"`
Expand Down
4 changes: 2 additions & 2 deletions null_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,10 @@ func TestNullUUIDUnmarshalJSON(t *testing.T) {
var nu NullUUID
err := json.Unmarshal(jsonNull, &nu)
if err != nil || nu.Valid {
t.Errorf("expected nil when unmarshaling null, got %s", err)
t.Errorf("expected nil when unmarshalling null, got %s", err)
}
err = json.Unmarshal(jsonUUID, &nu)
if err != nil || !nu.Valid {
t.Errorf("expected nil when unmarshaling null, got %s", err)
t.Errorf("expected nil when unmarshalling null, got %s", err)
}
}
21 changes: 16 additions & 5 deletions time.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,23 @@ func setClockSequence(seq int) {
}

// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1 and 2 UUIDs.
// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
func (uuid UUID) Time() Time {
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
return Time(time)
var t Time
switch uuid.Version() {
case 6:
time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110
t = Time(time)
case 7:
time := binary.BigEndian.Uint64(uuid[:8])
t = Time((time>>16)*10000 + g1582ns100)
default: // forward compatible
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
t = Time(time)
}
return t
}

// ClockSequence returns the clock sequence encoded in uuid.
Expand Down
79 changes: 74 additions & 5 deletions uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool {
return ok
}

// Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both
// the standard UUID forms defined in RFC 4122
// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition,
// Parse accepts non-standard strings such as the raw hex encoding
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings,
// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are
// examined in the latter case. Parse should not be used to validate strings as
// it parses non-standard encodings as indicated above.
func Parse(s string) (UUID, error) {
var uuid UUID
switch len(s) {
Expand Down Expand Up @@ -182,6 +186,59 @@ func Must(uuid UUID, err error) UUID {
return uuid
}

// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
// It returns an error if the format is invalid, otherwise nil.
func Validate(s string) error {
switch len(s) {
// Standard UUID format
case 36:

// UUID with "urn:uuid:" prefix
case 36 + 9:
if !strings.EqualFold(s[:9], "urn:uuid:") {
return fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]

// UUID enclosed in braces
case 36 + 2:
if s[0] != '{' || s[len(s)-1] != '}' {
return fmt.Errorf("invalid bracketed UUID format")
}
s = s[1 : len(s)-1]

// UUID without hyphens
case 32:
for i := 0; i < len(s); i += 2 {
_, ok := xtob(s[i], s[i+1])
if !ok {
return errors.New("invalid UUID format")
}
}

default:
return invalidLengthError{len(s)}
}

// Check for standard UUID format
if len(s) == 36 {
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return errors.New("invalid UUID format")
}
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
if _, ok := xtob(s[x], s[x+1]); !ok {
return errors.New("invalid UUID format")
}
}
}

return nil
}

// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
Expand Down Expand Up @@ -294,3 +351,15 @@ func DisableRandPool() {
poolMu.Lock()
poolPos = randPoolSize
}

// UUIDs is a slice of UUID types.
type UUIDs []UUID

// Strings returns a string slice containing the string form of each UUID in uuids.
func (uuids UUIDs) Strings() []string {
var uuidStrs = make([]string, len(uuids))
for i, uuid := range uuids {
uuidStrs[i] = uuid.String()
}
return uuidStrs
}

0 comments on commit 7e7e384

Please sign in to comment.