baggage.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. // Copyright The OpenTelemetry Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package baggage // import "go.opentelemetry.io/otel/baggage"
  15. import (
  16. "errors"
  17. "fmt"
  18. "net/url"
  19. "strings"
  20. "go.opentelemetry.io/otel/internal/baggage"
  21. )
  22. const (
  23. maxMembers = 180
  24. maxBytesPerMembers = 4096
  25. maxBytesPerBaggageString = 8192
  26. listDelimiter = ","
  27. keyValueDelimiter = "="
  28. propertyDelimiter = ";"
  29. )
  30. var (
  31. errInvalidKey = errors.New("invalid key")
  32. errInvalidValue = errors.New("invalid value")
  33. errInvalidProperty = errors.New("invalid baggage list-member property")
  34. errInvalidMember = errors.New("invalid baggage list-member")
  35. errMemberNumber = errors.New("too many list-members in baggage-string")
  36. errMemberBytes = errors.New("list-member too large")
  37. errBaggageBytes = errors.New("baggage-string too large")
  38. )
  39. // Property is an additional metadata entry for a baggage list-member.
  40. type Property struct {
  41. key, value string
  42. // hasValue indicates if a zero-value value means the property does not
  43. // have a value or if it was the zero-value.
  44. hasValue bool
  45. }
  46. // NewKeyProperty returns a new Property for key.
  47. //
  48. // If key is invalid, an error will be returned.
  49. func NewKeyProperty(key string) (Property, error) {
  50. if !validateKey(key) {
  51. return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
  52. }
  53. p := Property{key: key}
  54. return p, nil
  55. }
  56. // NewKeyValueProperty returns a new Property for key with value.
  57. //
  58. // The passed key must be compliant with W3C Baggage specification.
  59. // The passed value must be precent-encoded as defined in W3C Baggage specification.
  60. //
  61. // Notice: Consider using [NewKeyValuePropertyRaw] instead
  62. // that does not require precent-encoding of the value.
  63. func NewKeyValueProperty(key, value string) (Property, error) {
  64. if !validateValue(value) {
  65. return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
  66. }
  67. decodedValue, err := url.PathUnescape(value)
  68. if err != nil {
  69. return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
  70. }
  71. return NewKeyValuePropertyRaw(key, decodedValue)
  72. }
  73. // NewKeyValuePropertyRaw returns a new Property for key with value.
  74. //
  75. // The passed key must be compliant with W3C Baggage specification.
  76. func NewKeyValuePropertyRaw(key, value string) (Property, error) {
  77. if !validateKey(key) {
  78. return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
  79. }
  80. p := Property{
  81. key: key,
  82. value: value,
  83. hasValue: true,
  84. }
  85. return p, nil
  86. }
  87. func newInvalidProperty() Property {
  88. return Property{}
  89. }
  90. // parseProperty attempts to decode a Property from the passed string. It
  91. // returns an error if the input is invalid according to the W3C Baggage
  92. // specification.
  93. func parseProperty(property string) (Property, error) {
  94. if property == "" {
  95. return newInvalidProperty(), nil
  96. }
  97. p, ok := parsePropertyInternal(property)
  98. if !ok {
  99. return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property)
  100. }
  101. return p, nil
  102. }
  103. // validate ensures p conforms to the W3C Baggage specification, returning an
  104. // error otherwise.
  105. func (p Property) validate() error {
  106. errFunc := func(err error) error {
  107. return fmt.Errorf("invalid property: %w", err)
  108. }
  109. if !validateKey(p.key) {
  110. return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
  111. }
  112. if !p.hasValue && p.value != "" {
  113. return errFunc(errors.New("inconsistent value"))
  114. }
  115. return nil
  116. }
  117. // Key returns the Property key.
  118. func (p Property) Key() string {
  119. return p.key
  120. }
  121. // Value returns the Property value. Additionally, a boolean value is returned
  122. // indicating if the returned value is the empty if the Property has a value
  123. // that is empty or if the value is not set.
  124. func (p Property) Value() (string, bool) {
  125. return p.value, p.hasValue
  126. }
  127. // String encodes Property into a header string compliant with the W3C Baggage
  128. // specification.
  129. func (p Property) String() string {
  130. if p.hasValue {
  131. return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, valueEscape(p.value))
  132. }
  133. return p.key
  134. }
  135. type properties []Property
  136. func fromInternalProperties(iProps []baggage.Property) properties {
  137. if len(iProps) == 0 {
  138. return nil
  139. }
  140. props := make(properties, len(iProps))
  141. for i, p := range iProps {
  142. props[i] = Property{
  143. key: p.Key,
  144. value: p.Value,
  145. hasValue: p.HasValue,
  146. }
  147. }
  148. return props
  149. }
  150. func (p properties) asInternal() []baggage.Property {
  151. if len(p) == 0 {
  152. return nil
  153. }
  154. iProps := make([]baggage.Property, len(p))
  155. for i, prop := range p {
  156. iProps[i] = baggage.Property{
  157. Key: prop.key,
  158. Value: prop.value,
  159. HasValue: prop.hasValue,
  160. }
  161. }
  162. return iProps
  163. }
  164. func (p properties) Copy() properties {
  165. if len(p) == 0 {
  166. return nil
  167. }
  168. props := make(properties, len(p))
  169. copy(props, p)
  170. return props
  171. }
  172. // validate ensures each Property in p conforms to the W3C Baggage
  173. // specification, returning an error otherwise.
  174. func (p properties) validate() error {
  175. for _, prop := range p {
  176. if err := prop.validate(); err != nil {
  177. return err
  178. }
  179. }
  180. return nil
  181. }
  182. // String encodes properties into a header string compliant with the W3C Baggage
  183. // specification.
  184. func (p properties) String() string {
  185. props := make([]string, len(p))
  186. for i, prop := range p {
  187. props[i] = prop.String()
  188. }
  189. return strings.Join(props, propertyDelimiter)
  190. }
  191. // Member is a list-member of a baggage-string as defined by the W3C Baggage
  192. // specification.
  193. type Member struct {
  194. key, value string
  195. properties properties
  196. // hasData indicates whether the created property contains data or not.
  197. // Properties that do not contain data are invalid with no other check
  198. // required.
  199. hasData bool
  200. }
  201. // NewMemberRaw returns a new Member from the passed arguments.
  202. //
  203. // The passed key must be compliant with W3C Baggage specification.
  204. // The passed value must be precent-encoded as defined in W3C Baggage specification.
  205. //
  206. // Notice: Consider using [NewMemberRaw] instead
  207. // that does not require precent-encoding of the value.
  208. func NewMember(key, value string, props ...Property) (Member, error) {
  209. if !validateValue(value) {
  210. return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
  211. }
  212. decodedValue, err := url.PathUnescape(value)
  213. if err != nil {
  214. return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
  215. }
  216. return NewMemberRaw(key, decodedValue, props...)
  217. }
  218. // NewMemberRaw returns a new Member from the passed arguments.
  219. //
  220. // The passed key must be compliant with W3C Baggage specification.
  221. func NewMemberRaw(key, value string, props ...Property) (Member, error) {
  222. m := Member{
  223. key: key,
  224. value: value,
  225. properties: properties(props).Copy(),
  226. hasData: true,
  227. }
  228. if err := m.validate(); err != nil {
  229. return newInvalidMember(), err
  230. }
  231. return m, nil
  232. }
  233. func newInvalidMember() Member {
  234. return Member{}
  235. }
  236. // parseMember attempts to decode a Member from the passed string. It returns
  237. // an error if the input is invalid according to the W3C Baggage
  238. // specification.
  239. func parseMember(member string) (Member, error) {
  240. if n := len(member); n > maxBytesPerMembers {
  241. return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
  242. }
  243. var props properties
  244. keyValue, properties, found := strings.Cut(member, propertyDelimiter)
  245. if found {
  246. // Parse the member properties.
  247. for _, pStr := range strings.Split(properties, propertyDelimiter) {
  248. p, err := parseProperty(pStr)
  249. if err != nil {
  250. return newInvalidMember(), err
  251. }
  252. props = append(props, p)
  253. }
  254. }
  255. // Parse the member key/value pair.
  256. // Take into account a value can contain equal signs (=).
  257. k, v, found := strings.Cut(keyValue, keyValueDelimiter)
  258. if !found {
  259. return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
  260. }
  261. // "Leading and trailing whitespaces are allowed but MUST be trimmed
  262. // when converting the header into a data structure."
  263. key := strings.TrimSpace(k)
  264. if !validateKey(key) {
  265. return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
  266. }
  267. val := strings.TrimSpace(v)
  268. if !validateValue(val) {
  269. return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
  270. }
  271. // Decode a precent-encoded value.
  272. value, err := url.PathUnescape(val)
  273. if err != nil {
  274. return newInvalidMember(), fmt.Errorf("%w: %v", errInvalidValue, err)
  275. }
  276. return Member{key: key, value: value, properties: props, hasData: true}, nil
  277. }
  278. // validate ensures m conforms to the W3C Baggage specification.
  279. // A key must be an ASCII string, returning an error otherwise.
  280. func (m Member) validate() error {
  281. if !m.hasData {
  282. return fmt.Errorf("%w: %q", errInvalidMember, m)
  283. }
  284. if !validateKey(m.key) {
  285. return fmt.Errorf("%w: %q", errInvalidKey, m.key)
  286. }
  287. return m.properties.validate()
  288. }
  289. // Key returns the Member key.
  290. func (m Member) Key() string { return m.key }
  291. // Value returns the Member value.
  292. func (m Member) Value() string { return m.value }
  293. // Properties returns a copy of the Member properties.
  294. func (m Member) Properties() []Property { return m.properties.Copy() }
  295. // String encodes Member into a header string compliant with the W3C Baggage
  296. // specification.
  297. func (m Member) String() string {
  298. // A key is just an ASCII string. A value is restricted to be
  299. // US-ASCII characters excluding CTLs, whitespace,
  300. // DQUOTE, comma, semicolon, and backslash.
  301. s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, valueEscape(m.value))
  302. if len(m.properties) > 0 {
  303. s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
  304. }
  305. return s
  306. }
  307. // Baggage is a list of baggage members representing the baggage-string as
  308. // defined by the W3C Baggage specification.
  309. type Baggage struct { //nolint:golint
  310. list baggage.List
  311. }
  312. // New returns a new valid Baggage. It returns an error if it results in a
  313. // Baggage exceeding limits set in that specification.
  314. //
  315. // It expects all the provided members to have already been validated.
  316. func New(members ...Member) (Baggage, error) {
  317. if len(members) == 0 {
  318. return Baggage{}, nil
  319. }
  320. b := make(baggage.List)
  321. for _, m := range members {
  322. if !m.hasData {
  323. return Baggage{}, errInvalidMember
  324. }
  325. // OpenTelemetry resolves duplicates by last-one-wins.
  326. b[m.key] = baggage.Item{
  327. Value: m.value,
  328. Properties: m.properties.asInternal(),
  329. }
  330. }
  331. // Check member numbers after deduplication.
  332. if len(b) > maxMembers {
  333. return Baggage{}, errMemberNumber
  334. }
  335. bag := Baggage{b}
  336. if n := len(bag.String()); n > maxBytesPerBaggageString {
  337. return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
  338. }
  339. return bag, nil
  340. }
  341. // Parse attempts to decode a baggage-string from the passed string. It
  342. // returns an error if the input is invalid according to the W3C Baggage
  343. // specification.
  344. //
  345. // If there are duplicate list-members contained in baggage, the last one
  346. // defined (reading left-to-right) will be the only one kept. This diverges
  347. // from the W3C Baggage specification which allows duplicate list-members, but
  348. // conforms to the OpenTelemetry Baggage specification.
  349. func Parse(bStr string) (Baggage, error) {
  350. if bStr == "" {
  351. return Baggage{}, nil
  352. }
  353. if n := len(bStr); n > maxBytesPerBaggageString {
  354. return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
  355. }
  356. b := make(baggage.List)
  357. for _, memberStr := range strings.Split(bStr, listDelimiter) {
  358. m, err := parseMember(memberStr)
  359. if err != nil {
  360. return Baggage{}, err
  361. }
  362. // OpenTelemetry resolves duplicates by last-one-wins.
  363. b[m.key] = baggage.Item{
  364. Value: m.value,
  365. Properties: m.properties.asInternal(),
  366. }
  367. }
  368. // OpenTelemetry does not allow for duplicate list-members, but the W3C
  369. // specification does. Now that we have deduplicated, ensure the baggage
  370. // does not exceed list-member limits.
  371. if len(b) > maxMembers {
  372. return Baggage{}, errMemberNumber
  373. }
  374. return Baggage{b}, nil
  375. }
  376. // Member returns the baggage list-member identified by key.
  377. //
  378. // If there is no list-member matching the passed key the returned Member will
  379. // be a zero-value Member.
  380. // The returned member is not validated, as we assume the validation happened
  381. // when it was added to the Baggage.
  382. func (b Baggage) Member(key string) Member {
  383. v, ok := b.list[key]
  384. if !ok {
  385. // We do not need to worry about distinguishing between the situation
  386. // where a zero-valued Member is included in the Baggage because a
  387. // zero-valued Member is invalid according to the W3C Baggage
  388. // specification (it has an empty key).
  389. return newInvalidMember()
  390. }
  391. return Member{
  392. key: key,
  393. value: v.Value,
  394. properties: fromInternalProperties(v.Properties),
  395. hasData: true,
  396. }
  397. }
  398. // Members returns all the baggage list-members.
  399. // The order of the returned list-members does not have significance.
  400. //
  401. // The returned members are not validated, as we assume the validation happened
  402. // when they were added to the Baggage.
  403. func (b Baggage) Members() []Member {
  404. if len(b.list) == 0 {
  405. return nil
  406. }
  407. members := make([]Member, 0, len(b.list))
  408. for k, v := range b.list {
  409. members = append(members, Member{
  410. key: k,
  411. value: v.Value,
  412. properties: fromInternalProperties(v.Properties),
  413. hasData: true,
  414. })
  415. }
  416. return members
  417. }
  418. // SetMember returns a copy the Baggage with the member included. If the
  419. // baggage contains a Member with the same key the existing Member is
  420. // replaced.
  421. //
  422. // If member is invalid according to the W3C Baggage specification, an error
  423. // is returned with the original Baggage.
  424. func (b Baggage) SetMember(member Member) (Baggage, error) {
  425. if !member.hasData {
  426. return b, errInvalidMember
  427. }
  428. n := len(b.list)
  429. if _, ok := b.list[member.key]; !ok {
  430. n++
  431. }
  432. list := make(baggage.List, n)
  433. for k, v := range b.list {
  434. // Do not copy if we are just going to overwrite.
  435. if k == member.key {
  436. continue
  437. }
  438. list[k] = v
  439. }
  440. list[member.key] = baggage.Item{
  441. Value: member.value,
  442. Properties: member.properties.asInternal(),
  443. }
  444. return Baggage{list: list}, nil
  445. }
  446. // DeleteMember returns a copy of the Baggage with the list-member identified
  447. // by key removed.
  448. func (b Baggage) DeleteMember(key string) Baggage {
  449. n := len(b.list)
  450. if _, ok := b.list[key]; ok {
  451. n--
  452. }
  453. list := make(baggage.List, n)
  454. for k, v := range b.list {
  455. if k == key {
  456. continue
  457. }
  458. list[k] = v
  459. }
  460. return Baggage{list: list}
  461. }
  462. // Len returns the number of list-members in the Baggage.
  463. func (b Baggage) Len() int {
  464. return len(b.list)
  465. }
  466. // String encodes Baggage into a header string compliant with the W3C Baggage
  467. // specification.
  468. func (b Baggage) String() string {
  469. members := make([]string, 0, len(b.list))
  470. for k, v := range b.list {
  471. members = append(members, Member{
  472. key: k,
  473. value: v.Value,
  474. properties: fromInternalProperties(v.Properties),
  475. }.String())
  476. }
  477. return strings.Join(members, listDelimiter)
  478. }
  479. // parsePropertyInternal attempts to decode a Property from the passed string.
  480. // It follows the spec at https://www.w3.org/TR/baggage/#definition.
  481. func parsePropertyInternal(s string) (p Property, ok bool) {
  482. // For the entire function we will use " key = value " as an example.
  483. // Attempting to parse the key.
  484. // First skip spaces at the beginning "< >key = value " (they could be empty).
  485. index := skipSpace(s, 0)
  486. // Parse the key: " <key> = value ".
  487. keyStart := index
  488. keyEnd := index
  489. for _, c := range s[keyStart:] {
  490. if !validateKeyChar(c) {
  491. break
  492. }
  493. keyEnd++
  494. }
  495. // If we couldn't find any valid key character,
  496. // it means the key is either empty or invalid.
  497. if keyStart == keyEnd {
  498. return
  499. }
  500. // Skip spaces after the key: " key< >= value ".
  501. index = skipSpace(s, keyEnd)
  502. if index == len(s) {
  503. // A key can have no value, like: " key ".
  504. ok = true
  505. p.key = s[keyStart:keyEnd]
  506. return
  507. }
  508. // If we have not reached the end and we can't find the '=' delimiter,
  509. // it means the property is invalid.
  510. if s[index] != keyValueDelimiter[0] {
  511. return
  512. }
  513. // Attempting to parse the value.
  514. // Match: " key =< >value ".
  515. index = skipSpace(s, index+1)
  516. // Match the value string: " key = <value> ".
  517. // A valid property can be: " key =".
  518. // Therefore, we don't have to check if the value is empty.
  519. valueStart := index
  520. valueEnd := index
  521. for _, c := range s[valueStart:] {
  522. if !validateValueChar(c) {
  523. break
  524. }
  525. valueEnd++
  526. }
  527. // Skip all trailing whitespaces: " key = value< >".
  528. index = skipSpace(s, valueEnd)
  529. // If after looking for the value and skipping whitespaces
  530. // we have not reached the end, it means the property is
  531. // invalid, something like: " key = value value1".
  532. if index != len(s) {
  533. return
  534. }
  535. // Decode a precent-encoded value.
  536. value, err := url.PathUnescape(s[valueStart:valueEnd])
  537. if err != nil {
  538. return
  539. }
  540. ok = true
  541. p.key = s[keyStart:keyEnd]
  542. p.hasValue = true
  543. p.value = value
  544. return
  545. }
  546. func skipSpace(s string, offset int) int {
  547. i := offset
  548. for ; i < len(s); i++ {
  549. c := s[i]
  550. if c != ' ' && c != '\t' {
  551. break
  552. }
  553. }
  554. return i
  555. }
  556. func validateKey(s string) bool {
  557. if len(s) == 0 {
  558. return false
  559. }
  560. for _, c := range s {
  561. if !validateKeyChar(c) {
  562. return false
  563. }
  564. }
  565. return true
  566. }
  567. func validateKeyChar(c int32) bool {
  568. return (c >= 0x23 && c <= 0x27) ||
  569. (c >= 0x30 && c <= 0x39) ||
  570. (c >= 0x41 && c <= 0x5a) ||
  571. (c >= 0x5e && c <= 0x7a) ||
  572. c == 0x21 ||
  573. c == 0x2a ||
  574. c == 0x2b ||
  575. c == 0x2d ||
  576. c == 0x2e ||
  577. c == 0x7c ||
  578. c == 0x7e
  579. }
  580. func validateValue(s string) bool {
  581. for _, c := range s {
  582. if !validateValueChar(c) {
  583. return false
  584. }
  585. }
  586. return true
  587. }
  588. func validateValueChar(c int32) bool {
  589. return c == 0x21 ||
  590. (c >= 0x23 && c <= 0x2b) ||
  591. (c >= 0x2d && c <= 0x3a) ||
  592. (c >= 0x3c && c <= 0x5b) ||
  593. (c >= 0x5d && c <= 0x7e)
  594. }
  595. // valueEscape escapes the string so it can be safely placed inside a baggage value,
  596. // replacing special characters with %XX sequences as needed.
  597. //
  598. // The implementation is based on:
  599. // https://github.com/golang/go/blob/f6509cf5cdbb5787061b784973782933c47f1782/src/net/url/url.go#L285.
  600. func valueEscape(s string) string {
  601. hexCount := 0
  602. for i := 0; i < len(s); i++ {
  603. c := s[i]
  604. if shouldEscape(c) {
  605. hexCount++
  606. }
  607. }
  608. if hexCount == 0 {
  609. return s
  610. }
  611. var buf [64]byte
  612. var t []byte
  613. required := len(s) + 2*hexCount
  614. if required <= len(buf) {
  615. t = buf[:required]
  616. } else {
  617. t = make([]byte, required)
  618. }
  619. j := 0
  620. for i := 0; i < len(s); i++ {
  621. c := s[i]
  622. if shouldEscape(s[i]) {
  623. const upperhex = "0123456789ABCDEF"
  624. t[j] = '%'
  625. t[j+1] = upperhex[c>>4]
  626. t[j+2] = upperhex[c&15]
  627. j += 3
  628. } else {
  629. t[j] = c
  630. j++
  631. }
  632. }
  633. return string(t)
  634. }
  635. // shouldEscape returns true if the specified byte should be escaped when
  636. // appearing in a baggage value string.
  637. func shouldEscape(c byte) bool {
  638. if c == '%' {
  639. // The percent character must be encoded so that percent-encoding can work.
  640. return true
  641. }
  642. return !validateValueChar(int32(c))
  643. }