本站源代码
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.

83 lines
2.6KB

  1. // Copyright 2015 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. package unicode
  5. import (
  6. "golang.org/x/text/transform"
  7. )
  8. // BOMOverride returns a new decoder transformer that is identical to fallback,
  9. // except that the presence of a Byte Order Mark at the start of the input
  10. // causes it to switch to the corresponding Unicode decoding. It will only
  11. // consider BOMs for UTF-8, UTF-16BE, and UTF-16LE.
  12. //
  13. // This differs from using ExpectBOM by allowing a BOM to switch to UTF-8, not
  14. // just UTF-16 variants, and allowing falling back to any encoding scheme.
  15. //
  16. // This technique is recommended by the W3C for use in HTML 5: "For
  17. // compatibility with deployed content, the byte order mark (also known as BOM)
  18. // is considered more authoritative than anything else."
  19. // http://www.w3.org/TR/encoding/#specification-hooks
  20. //
  21. // Using BOMOverride is mostly intended for use cases where the first characters
  22. // of a fallback encoding are known to not be a BOM, for example, for valid HTML
  23. // and most encodings.
  24. func BOMOverride(fallback transform.Transformer) transform.Transformer {
  25. // TODO: possibly allow a variadic argument of unicode encodings to allow
  26. // specifying details of which fallbacks are supported as well as
  27. // specifying the details of the implementations. This would also allow for
  28. // support for UTF-32, which should not be supported by default.
  29. return &bomOverride{fallback: fallback}
  30. }
  31. type bomOverride struct {
  32. fallback transform.Transformer
  33. current transform.Transformer
  34. }
  35. func (d *bomOverride) Reset() {
  36. d.current = nil
  37. d.fallback.Reset()
  38. }
  39. var (
  40. // TODO: we could use decode functions here, instead of allocating a new
  41. // decoder on every NewDecoder as IgnoreBOM decoders can be stateless.
  42. utf16le = UTF16(LittleEndian, IgnoreBOM)
  43. utf16be = UTF16(BigEndian, IgnoreBOM)
  44. )
  45. const utf8BOM = "\ufeff"
  46. func (d *bomOverride) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
  47. if d.current != nil {
  48. return d.current.Transform(dst, src, atEOF)
  49. }
  50. if len(src) < 3 && !atEOF {
  51. return 0, 0, transform.ErrShortSrc
  52. }
  53. d.current = d.fallback
  54. bomSize := 0
  55. if len(src) >= 2 {
  56. if src[0] == 0xFF && src[1] == 0xFE {
  57. d.current = utf16le.NewDecoder()
  58. bomSize = 2
  59. } else if src[0] == 0xFE && src[1] == 0xFF {
  60. d.current = utf16be.NewDecoder()
  61. bomSize = 2
  62. } else if len(src) >= 3 &&
  63. src[0] == utf8BOM[0] &&
  64. src[1] == utf8BOM[1] &&
  65. src[2] == utf8BOM[2] {
  66. d.current = transform.Nop
  67. bomSize = 3
  68. }
  69. }
  70. if bomSize < len(src) {
  71. nDst, nSrc, err = d.current.Transform(dst, src[bomSize:], atEOF)
  72. }
  73. return nDst, nSrc + bomSize, err
  74. }
上海开阖软件有限公司 沪ICP备12045867号-1