|
| 1 | +// Copyright 2023 The Go Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +//go:build goexperiment.jsonv2 |
| 6 | + |
| 7 | +// jsonflags implements all the optional boolean flags. |
| 8 | +// These flags are shared across both "json", "jsontext", and "jsonopts". |
| 9 | +package jsonflags |
| 10 | + |
| 11 | +import "encoding/json/internal" |
| 12 | + |
| 13 | +// Bools represents zero or more boolean flags, all set to true or false. |
| 14 | +// The least-significant bit is the boolean value of all flags in the set. |
| 15 | +// The remaining bits identify which particular flags. |
| 16 | +// |
| 17 | +// In common usage, this is OR'd with 0 or 1. For example: |
| 18 | +// - (AllowInvalidUTF8 | 0) means "AllowInvalidUTF8 is false" |
| 19 | +// - (Multiline | Indent | 1) means "Multiline and Indent are true" |
| 20 | +type Bools uint64 |
| 21 | + |
| 22 | +func (Bools) JSONOptions(internal.NotForPublicUse) {} |
| 23 | + |
| 24 | +const ( |
| 25 | + // AllFlags is the set of all flags. |
| 26 | + AllFlags = AllCoderFlags | AllArshalV2Flags | AllArshalV1Flags |
| 27 | + |
| 28 | + // AllCoderFlags is the set of all encoder/decoder flags. |
| 29 | + AllCoderFlags = (maxCoderFlag - 1) - initFlag |
| 30 | + |
| 31 | + // AllArshalV2Flags is the set of all v2 marshal/unmarshal flags. |
| 32 | + AllArshalV2Flags = (maxArshalV2Flag - 1) - (maxCoderFlag - 1) |
| 33 | + |
| 34 | + // AllArshalV1Flags is the set of all v1 marshal/unmarshal flags. |
| 35 | + AllArshalV1Flags = (maxArshalV1Flag - 1) - (maxArshalV2Flag - 1) |
| 36 | + |
| 37 | + // NonBooleanFlags is the set of non-boolean flags, |
| 38 | + // where the value is some other concrete Go type. |
| 39 | + // The value of the flag is stored within jsonopts.Struct. |
| 40 | + NonBooleanFlags = 0 | |
| 41 | + Indent | |
| 42 | + IndentPrefix | |
| 43 | + ByteLimit | |
| 44 | + DepthLimit | |
| 45 | + Marshalers | |
| 46 | + Unmarshalers |
| 47 | + |
| 48 | + // DefaultV1Flags is the set of booleans flags that default to true under |
| 49 | + // v1 semantics. None of the non-boolean flags differ between v1 and v2. |
| 50 | + DefaultV1Flags = 0 | |
| 51 | + AllowDuplicateNames | |
| 52 | + AllowInvalidUTF8 | |
| 53 | + EscapeForHTML | |
| 54 | + EscapeForJS | |
| 55 | + EscapeInvalidUTF8 | |
| 56 | + PreserveRawStrings | |
| 57 | + Deterministic | |
| 58 | + FormatNilMapAsNull | |
| 59 | + FormatNilSliceAsNull | |
| 60 | + MatchCaseInsensitiveNames | |
| 61 | + CallMethodsWithLegacySemantics | |
| 62 | + FormatBytesWithLegacySemantics | |
| 63 | + FormatTimeWithLegacySemantics | |
| 64 | + MatchCaseSensitiveDelimiter | |
| 65 | + MergeWithLegacySemantics | |
| 66 | + OmitEmptyWithLegacyDefinition | |
| 67 | + ReportErrorsWithLegacySemantics | |
| 68 | + StringifyWithLegacySemantics | |
| 69 | + UnmarshalArrayFromAnyLength |
| 70 | + |
| 71 | + // AnyWhitespace reports whether the encoded output might have any whitespace. |
| 72 | + AnyWhitespace = Multiline | SpaceAfterColon | SpaceAfterComma |
| 73 | + |
| 74 | + // WhitespaceFlags is the set of flags related to whitespace formatting. |
| 75 | + // In contrast to AnyWhitespace, this includes Indent and IndentPrefix |
| 76 | + // as those settings take no effect if Multiline is false. |
| 77 | + WhitespaceFlags = AnyWhitespace | Indent | IndentPrefix |
| 78 | + |
| 79 | + // AnyEscape is the set of flags related to escaping in a JSON string. |
| 80 | + AnyEscape = EscapeForHTML | EscapeForJS | EscapeInvalidUTF8 |
| 81 | + |
| 82 | + // CanonicalizeNumbers is the set of flags related to raw number canonicalization. |
| 83 | + CanonicalizeNumbers = CanonicalizeRawInts | CanonicalizeRawFloats |
| 84 | +) |
| 85 | + |
| 86 | +// Encoder and decoder flags. |
| 87 | +const ( |
| 88 | + initFlag Bools = 1 << iota // reserved for the boolean value itself |
| 89 | + |
| 90 | + AllowDuplicateNames // encode or decode |
| 91 | + AllowInvalidUTF8 // encode or decode |
| 92 | + WithinArshalCall // encode or decode; for internal use by json.Marshal and json.Unmarshal |
| 93 | + OmitTopLevelNewline // encode only; for internal use by json.Marshal and json.MarshalWrite |
| 94 | + PreserveRawStrings // encode only |
| 95 | + CanonicalizeRawInts // encode only |
| 96 | + CanonicalizeRawFloats // encode only |
| 97 | + ReorderRawObjects // encode only |
| 98 | + EscapeForHTML // encode only |
| 99 | + EscapeForJS // encode only |
| 100 | + EscapeInvalidUTF8 // encode only; only exposed in v1 |
| 101 | + Multiline // encode only |
| 102 | + SpaceAfterColon // encode only |
| 103 | + SpaceAfterComma // encode only |
| 104 | + Indent // encode only; non-boolean flag |
| 105 | + IndentPrefix // encode only; non-boolean flag |
| 106 | + ByteLimit // encode or decode; non-boolean flag |
| 107 | + DepthLimit // encode or decode; non-boolean flag |
| 108 | + |
| 109 | + maxCoderFlag |
| 110 | +) |
| 111 | + |
| 112 | +// Marshal and Unmarshal flags (for v2). |
| 113 | +const ( |
| 114 | + _ Bools = (maxCoderFlag >> 1) << iota |
| 115 | + |
| 116 | + StringifyNumbers // marshal or unmarshal |
| 117 | + Deterministic // marshal only |
| 118 | + FormatNilMapAsNull // marshal only |
| 119 | + FormatNilSliceAsNull // marshal only |
| 120 | + OmitZeroStructFields // marshal only |
| 121 | + MatchCaseInsensitiveNames // marshal or unmarshal |
| 122 | + DiscardUnknownMembers // marshal only |
| 123 | + RejectUnknownMembers // unmarshal only |
| 124 | + Marshalers // marshal only; non-boolean flag |
| 125 | + Unmarshalers // unmarshal only; non-boolean flag |
| 126 | + |
| 127 | + maxArshalV2Flag |
| 128 | +) |
| 129 | + |
| 130 | +// Marshal and Unmarshal flags (for v1). |
| 131 | +const ( |
| 132 | + _ Bools = (maxArshalV2Flag >> 1) << iota |
| 133 | + |
| 134 | + CallMethodsWithLegacySemantics // marshal or unmarshal |
| 135 | + FormatBytesWithLegacySemantics // marshal or unmarshal |
| 136 | + FormatTimeWithLegacySemantics // marshal or unmarshal |
| 137 | + MatchCaseSensitiveDelimiter // marshal or unmarshal |
| 138 | + MergeWithLegacySemantics // unmarshal |
| 139 | + OmitEmptyWithLegacyDefinition // marshal |
| 140 | + ReportErrorsWithLegacySemantics // marshal or unmarshal |
| 141 | + StringifyWithLegacySemantics // marshal or unmarshal |
| 142 | + StringifyBoolsAndStrings // marshal or unmarshal; for internal use by jsonv2.makeStructArshaler |
| 143 | + UnmarshalAnyWithRawNumber // unmarshal; for internal use by jsonv1.Decoder.UseNumber |
| 144 | + UnmarshalArrayFromAnyLength // unmarshal |
| 145 | + |
| 146 | + maxArshalV1Flag |
| 147 | +) |
| 148 | + |
| 149 | +// Flags is a set of boolean flags. |
| 150 | +// If the presence bit is zero, then the value bit must also be zero. |
| 151 | +// The least-significant bit of both fields is always zero. |
| 152 | +// |
| 153 | +// Unlike Bools, which can represent a set of bools that are all true or false, |
| 154 | +// Flags represents a set of bools, each individually may be true or false. |
| 155 | +type Flags struct{ Presence, Values uint64 } |
| 156 | + |
| 157 | +// Join joins two sets of flags such that the latter takes precedence. |
| 158 | +func (dst *Flags) Join(src Flags) { |
| 159 | + // Copy over all source presence bits over to the destination (using OR), |
| 160 | + // then invert the source presence bits to clear out source value (using AND-NOT), |
| 161 | + // then copy over source value bits over to the destination (using OR). |
| 162 | + // e.g., dst := Flags{Presence: 0b_1100_0011, Value: 0b_1000_0011} |
| 163 | + // e.g., src := Flags{Presence: 0b_0101_1010, Value: 0b_1001_0010} |
| 164 | + dst.Presence |= src.Presence // e.g., 0b_1100_0011 | 0b_0101_1010 -> 0b_110_11011 |
| 165 | + dst.Values &= ^src.Presence // e.g., 0b_1000_0011 & 0b_1010_0101 -> 0b_100_00001 |
| 166 | + dst.Values |= src.Values // e.g., 0b_1000_0001 | 0b_1001_0010 -> 0b_100_10011 |
| 167 | +} |
| 168 | + |
| 169 | +// Set sets both the presence and value for the provided bool (or set of bools). |
| 170 | +func (fs *Flags) Set(f Bools) { |
| 171 | + // Select out the bits for the flag identifiers (everything except LSB), |
| 172 | + // then set the presence for all the identifier bits (using OR), |
| 173 | + // then invert the identifier bits to clear out the values (using AND-NOT), |
| 174 | + // then copy over all the identifier bits to the value if LSB is 1. |
| 175 | + // e.g., fs := Flags{Presence: 0b_0101_0010, Value: 0b_0001_0010} |
| 176 | + // e.g., f := 0b_1001_0001 |
| 177 | + id := uint64(f) &^ uint64(1) // e.g., 0b_1001_0001 & 0b_1111_1110 -> 0b_1001_0000 |
| 178 | + fs.Presence |= id // e.g., 0b_0101_0010 | 0b_1001_0000 -> 0b_1101_0011 |
| 179 | + fs.Values &= ^id // e.g., 0b_0001_0010 & 0b_0110_1111 -> 0b_0000_0010 |
| 180 | + fs.Values |= uint64(f&1) * id // e.g., 0b_0000_0010 | 0b_1001_0000 -> 0b_1001_0010 |
| 181 | +} |
| 182 | + |
| 183 | +// Get reports whether the bool (or any of the bools) is true. |
| 184 | +// This is generally only used with a singular bool. |
| 185 | +// The value bit of f (i.e., the LSB) is ignored. |
| 186 | +func (fs Flags) Get(f Bools) bool { |
| 187 | + return fs.Values&uint64(f) > 0 |
| 188 | +} |
| 189 | + |
| 190 | +// Has reports whether the bool (or any of the bools) is set. |
| 191 | +// The value bit of f (i.e., the LSB) is ignored. |
| 192 | +func (fs Flags) Has(f Bools) bool { |
| 193 | + return fs.Presence&uint64(f) > 0 |
| 194 | +} |
| 195 | + |
| 196 | +// Clear clears both the presence and value for the provided bool or bools. |
| 197 | +// The value bit of f (i.e., the LSB) is ignored. |
| 198 | +func (fs *Flags) Clear(f Bools) { |
| 199 | + // Invert f to produce a mask to clear all bits in f (using AND). |
| 200 | + // e.g., fs := Flags{Presence: 0b_0101_0010, Value: 0b_0001_0010} |
| 201 | + // e.g., f := 0b_0001_1000 |
| 202 | + mask := uint64(^f) // e.g., 0b_0001_1000 -> 0b_1110_0111 |
| 203 | + fs.Presence &= mask // e.g., 0b_0101_0010 & 0b_1110_0111 -> 0b_0100_0010 |
| 204 | + fs.Values &= mask // e.g., 0b_0001_0010 & 0b_1110_0111 -> 0b_0000_0010 |
| 205 | +} |
0 commit comments