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