You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
8.5 KiB
215 lines
8.5 KiB
// Copyright 2023 The Go Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
//go:build goexperiment.jsonv2 |
|
|
|
// jsonflags implements all the optional boolean flags. |
|
// These flags are shared across both "json", "jsontext", and "jsonopts". |
|
package jsonflags |
|
|
|
import "next.orly.dev/pkg/json/internal" |
|
|
|
// Bools represents zero or more boolean flags, all set to true or false. |
|
// The least-significant bit is the boolean value of all flags in the set. |
|
// The remaining bits identify which particular flags. |
|
// |
|
// In common usage, this is OR'd with 0 or 1. For example: |
|
// - (AllowInvalidUTF8 | 0) means "AllowInvalidUTF8 is false" |
|
// - (Multiline | Indent | 1) means "Multiline and Indent are true" |
|
type Bools uint64 |
|
|
|
func (Bools) JSONOptions(internal.NotForPublicUse) {} |
|
|
|
const ( |
|
// AllFlags is the set of all flags. |
|
AllFlags = AllCoderFlags | AllArshalV2Flags | AllArshalV1Flags |
|
|
|
// AllCoderFlags is the set of all encoder/decoder flags. |
|
AllCoderFlags = (maxCoderFlag - 1) - initFlag |
|
|
|
// AllArshalV2Flags is the set of all v2 marshal/unmarshal flags. |
|
AllArshalV2Flags = (maxArshalV2Flag - 1) - (maxCoderFlag - 1) |
|
|
|
// AllArshalV1Flags is the set of all v1 marshal/unmarshal flags. |
|
AllArshalV1Flags = (maxArshalV1Flag - 1) - (maxArshalV2Flag - 1) |
|
|
|
// NonBooleanFlags is the set of non-boolean flags, |
|
// where the value is some other concrete Go type. |
|
// The value of the flag is stored within jsonopts.Struct. |
|
NonBooleanFlags = 0 | |
|
Indent | |
|
IndentPrefix | |
|
ByteLimit | |
|
DepthLimit | |
|
Marshalers | |
|
Unmarshalers |
|
|
|
// DefaultV1Flags is the set of booleans flags that default to true under |
|
// v1 semantics. None of the non-boolean flags differ between v1 and v2. |
|
DefaultV1Flags = 0 | |
|
AllowDuplicateNames | |
|
AllowInvalidUTF8 | |
|
EscapeForHTML | |
|
EscapeForJS | |
|
PreserveRawStrings | |
|
Deterministic | |
|
FormatNilMapAsNull | |
|
FormatNilSliceAsNull | |
|
MatchCaseInsensitiveNames | |
|
CallMethodsWithLegacySemantics | |
|
FormatByteArrayAsArray | |
|
FormatBytesWithLegacySemantics | |
|
FormatDurationAsNano | |
|
MatchCaseSensitiveDelimiter | |
|
MergeWithLegacySemantics | |
|
OmitEmptyWithLegacySemantics | |
|
ParseBytesWithLooseRFC4648 | |
|
ParseTimeWithLooseRFC3339 | |
|
ReportErrorsWithLegacySemantics | |
|
StringifyWithLegacySemantics | |
|
UnmarshalArrayFromAnyLength |
|
|
|
// AnyWhitespace reports whether the encoded output might have any whitespace. |
|
AnyWhitespace = Multiline | SpaceAfterColon | SpaceAfterComma |
|
|
|
// WhitespaceFlags is the set of flags related to whitespace formatting. |
|
// In contrast to AnyWhitespace, this includes Indent and IndentPrefix |
|
// as those settings take no effect if Multiline is false. |
|
WhitespaceFlags = AnyWhitespace | Indent | IndentPrefix |
|
|
|
// AnyEscape is the set of flags related to escaping in a JSON string. |
|
AnyEscape = EscapeForHTML | EscapeForJS |
|
|
|
// CanonicalizeNumbers is the set of flags related to raw number canonicalization. |
|
CanonicalizeNumbers = CanonicalizeRawInts | CanonicalizeRawFloats |
|
) |
|
|
|
// Encoder and decoder flags. |
|
const ( |
|
initFlag Bools = 1 << iota // reserved for the boolean value itself |
|
|
|
AllowDuplicateNames // encode or decode |
|
AllowInvalidUTF8 // encode or decode |
|
WithinArshalCall // encode or decode; for internal use by json.Marshal and json.Unmarshal |
|
OmitTopLevelNewline // encode only; for internal use by json.Marshal and json.MarshalWrite |
|
PreserveRawStrings // encode only |
|
CanonicalizeRawInts // encode only |
|
CanonicalizeRawFloats // encode only |
|
ReorderRawObjects // encode only |
|
EscapeForHTML // encode only |
|
EscapeForJS // encode only |
|
Multiline // encode only |
|
SpaceAfterColon // encode only |
|
SpaceAfterComma // encode only |
|
Indent // encode only; non-boolean flag |
|
IndentPrefix // encode only; non-boolean flag |
|
ByteLimit // encode or decode; non-boolean flag |
|
DepthLimit // encode or decode; non-boolean flag |
|
|
|
maxCoderFlag |
|
) |
|
|
|
// Marshal and Unmarshal flags (for v2). |
|
const ( |
|
_ Bools = (maxCoderFlag >> 1) << iota |
|
|
|
StringifyNumbers // marshal or unmarshal |
|
Deterministic // marshal only |
|
FormatNilMapAsNull // marshal only |
|
FormatNilSliceAsNull // marshal only |
|
OmitZeroStructFields // marshal only |
|
MatchCaseInsensitiveNames // marshal or unmarshal |
|
DiscardUnknownMembers // marshal only |
|
RejectUnknownMembers // unmarshal only |
|
Marshalers // marshal only; non-boolean flag |
|
Unmarshalers // unmarshal only; non-boolean flag |
|
|
|
maxArshalV2Flag |
|
) |
|
|
|
// Marshal and Unmarshal flags (for v1). |
|
const ( |
|
_ Bools = (maxArshalV2Flag >> 1) << iota |
|
|
|
CallMethodsWithLegacySemantics // marshal or unmarshal |
|
FormatByteArrayAsArray // marshal or unmarshal |
|
FormatBytesWithLegacySemantics // marshal or unmarshal |
|
FormatDurationAsNano // marshal or unmarshal |
|
MatchCaseSensitiveDelimiter // marshal or unmarshal |
|
MergeWithLegacySemantics // unmarshal |
|
OmitEmptyWithLegacySemantics // marshal |
|
ParseBytesWithLooseRFC4648 // unmarshal |
|
ParseTimeWithLooseRFC3339 // unmarshal |
|
ReportErrorsWithLegacySemantics // marshal or unmarshal |
|
StringifyWithLegacySemantics // marshal or unmarshal |
|
StringifyBoolsAndStrings // marshal or unmarshal; for internal use by jsonv2.makeStructArshaler |
|
UnmarshalAnyWithRawNumber // unmarshal; for internal use by jsonv1.Decoder.UseNumber |
|
UnmarshalArrayFromAnyLength // unmarshal |
|
|
|
maxArshalV1Flag |
|
) |
|
|
|
// bitsUsed is the number of bits used in the 64-bit boolean flags |
|
const bitsUsed = 42 |
|
|
|
// Static compile check that bitsUsed and maxArshalV1Flag are in sync. |
|
const _ = uint64((1<<bitsUsed)-maxArshalV1Flag) + uint64(maxArshalV1Flag-(1<<bitsUsed)) |
|
|
|
// Flags is a set of boolean flags. |
|
// If the presence bit is zero, then the value bit must also be zero. |
|
// The least-significant bit of both fields is always zero. |
|
// |
|
// Unlike Bools, which can represent a set of bools that are all true or false, |
|
// Flags represents a set of bools, each individually may be true or false. |
|
type Flags struct{ Presence, Values uint64 } |
|
|
|
// Join joins two sets of flags such that the latter takes precedence. |
|
func (dst *Flags) Join(src Flags) { |
|
// Copy over all source presence bits over to the destination (using OR), |
|
// then invert the source presence bits to clear out source value (using AND-NOT), |
|
// then copy over source value bits over to the destination (using OR). |
|
// e.g., dst := Flags{Presence: 0b_1100_0011, Value: 0b_1000_0011} |
|
// e.g., src := Flags{Presence: 0b_0101_1010, Value: 0b_1001_0010} |
|
dst.Presence |= src.Presence // e.g., 0b_1100_0011 | 0b_0101_1010 -> 0b_110_11011 |
|
dst.Values &= ^src.Presence // e.g., 0b_1000_0011 & 0b_1010_0101 -> 0b_100_00001 |
|
dst.Values |= src.Values // e.g., 0b_1000_0001 | 0b_1001_0010 -> 0b_100_10011 |
|
} |
|
|
|
// Set sets both the presence and value for the provided bool (or set of bools). |
|
func (fs *Flags) Set(f Bools) { |
|
// Select out the bits for the flag identifiers (everything except LSB), |
|
// then set the presence for all the identifier bits (using OR), |
|
// then invert the identifier bits to clear out the values (using AND-NOT), |
|
// then copy over all the identifier bits to the value if LSB is 1. |
|
// e.g., fs := Flags{Presence: 0b_0101_0010, Value: 0b_0001_0010} |
|
// e.g., f := 0b_1001_0001 |
|
id := uint64(f) &^ uint64(1) // e.g., 0b_1001_0001 & 0b_1111_1110 -> 0b_1001_0000 |
|
fs.Presence |= id // e.g., 0b_0101_0010 | 0b_1001_0000 -> 0b_1101_0011 |
|
fs.Values &= ^id // e.g., 0b_0001_0010 & 0b_0110_1111 -> 0b_0000_0010 |
|
fs.Values |= uint64(f&1) * id // e.g., 0b_0000_0010 | 0b_1001_0000 -> 0b_1001_0010 |
|
} |
|
|
|
// Get reports whether the bool (or any of the bools) is true. |
|
// This is generally only used with a singular bool. |
|
// The value bit of f (i.e., the LSB) is ignored. |
|
func (fs Flags) Get(f Bools) bool { |
|
return fs.Values&uint64(f) > 0 |
|
} |
|
|
|
// Has reports whether the bool (or any of the bools) is set. |
|
// The value bit of f (i.e., the LSB) is ignored. |
|
func (fs Flags) Has(f Bools) bool { |
|
return fs.Presence&uint64(f) > 0 |
|
} |
|
|
|
// Clear clears both the presence and value for the provided bool or bools. |
|
// The value bit of f (i.e., the LSB) is ignored. |
|
func (fs *Flags) Clear(f Bools) { |
|
// Invert f to produce a mask to clear all bits in f (using AND). |
|
// e.g., fs := Flags{Presence: 0b_0101_0010, Value: 0b_0001_0010} |
|
// e.g., f := 0b_0001_1000 |
|
mask := uint64(^f) // e.g., 0b_0001_1000 -> 0b_1110_0111 |
|
fs.Presence &= mask // e.g., 0b_0101_0010 & 0b_1110_0111 -> 0b_0100_0010 |
|
fs.Values &= mask // e.g., 0b_0001_0010 & 0b_1110_0111 -> 0b_0000_0010 |
|
}
|
|
|