localereader_windows.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // +build windows
  2. package localereader
  3. import (
  4. "io"
  5. "syscall"
  6. "unicode/utf8"
  7. "unsafe"
  8. "golang.org/x/text/transform"
  9. )
  10. const (
  11. CP_ACP = 0
  12. MB_ERR_INVALID_CHARS = 8
  13. )
  14. var (
  15. modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  16. procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
  17. procIsDBCSLeadByte = modkernel32.NewProc("IsDBCSLeadByte")
  18. )
  19. type codepageDecoder struct {
  20. transform.NopResetter
  21. cp int
  22. }
  23. func (codepageDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
  24. r, size := rune(0), 0
  25. loop:
  26. for ; nSrc < len(src); nSrc += size {
  27. switch c0 := src[nSrc]; {
  28. case c0 < utf8.RuneSelf:
  29. r, size = rune(c0), 1
  30. default:
  31. br, _, _ := procIsDBCSLeadByte.Call(uintptr(src[nSrc]))
  32. if br == 0 {
  33. r = rune(src[nSrc])
  34. size = 1
  35. break
  36. }
  37. if nSrc >= len(src)-1 {
  38. r = rune(src[nSrc])
  39. size = 1
  40. break
  41. }
  42. n, _, _ := procMultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&src[nSrc])), uintptr(2), uintptr(0), 0)
  43. if n <= 0 {
  44. err = syscall.GetLastError()
  45. break
  46. }
  47. var us [1]uint16
  48. rc, _, _ := procMultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&src[nSrc])), uintptr(2), uintptr(unsafe.Pointer(&us[0])), 1)
  49. if rc == 0 {
  50. size = 1
  51. break
  52. }
  53. r = rune(us[0])
  54. size = 2
  55. }
  56. if nDst+utf8.RuneLen(r) > len(dst) {
  57. err = transform.ErrShortDst
  58. break loop
  59. }
  60. nDst += utf8.EncodeRune(dst[nDst:], r)
  61. }
  62. return nDst, nSrc, err
  63. }
  64. func newReader(r io.Reader) io.Reader {
  65. return transform.NewReader(r, NewAcpDecoder())
  66. }
  67. func NewCodePageDecoder(cp int) transform.Transformer {
  68. return &codepageDecoder{cp: cp}
  69. }
  70. func NewAcpDecoder() transform.Transformer {
  71. return &codepageDecoder{cp: CP_ACP}
  72. }