Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

150 řádky
4.5 KiB

  1. package iconv
  2. /*
  3. #cgo darwin LDFLAGS: -liconv
  4. #cgo freebsd LDFLAGS: -liconv
  5. #cgo windows LDFLAGS: -liconv
  6. #include <stdlib.h>
  7. #include <iconv.h>
  8. #include <locale.h>
  9. // As of GO 1.6 passing a pointer to Go pointer, will lead to panic
  10. // Therofore we use this wrapper function, to avoid passing **char directly from go
  11. size_t call_iconv(iconv_t ctx, char *in, size_t *size_in, char *out, size_t *size_out){
  12. return iconv(ctx, &in, size_in, &out, size_out);
  13. }
  14. */
  15. import "C"
  16. import "syscall"
  17. import "unsafe"
  18. type Converter struct {
  19. context C.iconv_t
  20. }
  21. // Initialize a new Converter. If fromEncoding or toEncoding are not supported by
  22. // iconv then an EINVAL error will be returned. An ENOMEM error maybe returned if
  23. // there is not enough memory to initialize an iconv descriptor
  24. func NewConverter(fromEncoding string, toEncoding string) (*Converter, error) {
  25. // convert to C strings
  26. toEncodingC := C.CString(toEncoding)
  27. fromEncodingC := C.CString(fromEncoding)
  28. // open an iconv descriptor
  29. context, err := C.iconv_open(toEncodingC, fromEncodingC)
  30. // free the C Strings
  31. C.free(unsafe.Pointer(toEncodingC))
  32. C.free(unsafe.Pointer(fromEncodingC))
  33. if err != nil {
  34. return nil, err
  35. }
  36. return &Converter{context}, nil
  37. }
  38. // Close a Converter's iconv descriptor explicitly
  39. func (converter *Converter) Close() error {
  40. _, err := C.iconv_close(converter.context)
  41. return err
  42. }
  43. // Reset state of iconv context
  44. func (converter *Converter) Reset() error {
  45. _, _, err := converter.Convert(nil, nil)
  46. return err
  47. }
  48. // Convert bytes from an input byte slice into a give output byte slice
  49. //
  50. // As many bytes that can converted and fit into the size of output will be
  51. // processed and the number of bytes read for input as well as the number of
  52. // bytes written to output will be returned. If not all converted bytes can fit
  53. // into output and E2BIG error will also be returned. If input contains an invalid
  54. // sequence of bytes for the Converter's fromEncoding an EILSEQ error will be returned
  55. //
  56. // For shift based output encodings, any end shift byte sequences can be generated by
  57. // passing a 0 length byte slice as input. Also passing a 0 length byte slice for output
  58. // will simply reset the iconv descriptor shift state without writing any bytes.
  59. func (converter *Converter) Convert(input []byte, output []byte) (bytesRead int, bytesWritten int, err error) {
  60. inputLeft := C.size_t(len(input))
  61. outputLeft := C.size_t(len(output))
  62. var inputPointer, outputPointer *C.char
  63. if inputLeft > 0 {
  64. inputPointer = (*C.char)(unsafe.Pointer(&input[0]))
  65. }
  66. if outputLeft > 0 {
  67. outputPointer = (*C.char)(unsafe.Pointer(&output[0]))
  68. }
  69. _, err = C.call_iconv(converter.context, inputPointer, &inputLeft, outputPointer, &outputLeft)
  70. bytesRead = len(input) - int(inputLeft)
  71. bytesWritten = len(output) - int(outputLeft)
  72. return bytesRead, bytesWritten, err
  73. }
  74. // Convert an input string
  75. //
  76. // EILSEQ error may be returned if input contains invalid bytes for the Converter's fromEncoding
  77. func (converter *Converter) ConvertString(input string) (output string, err error) {
  78. // construct the buffers
  79. inputBuffer := []byte(input)
  80. outputBuffer := make([]byte, len(inputBuffer)*2) // we use a larger buffer to help avoid resizing later
  81. // call Convert until all input bytes are read or an error occurs
  82. var bytesRead, totalBytesRead, bytesWritten, totalBytesWritten int
  83. for totalBytesRead < len(inputBuffer) && err == nil {
  84. // use the totals to create buffer slices
  85. bytesRead, bytesWritten, err = converter.Convert(inputBuffer[totalBytesRead:], outputBuffer[totalBytesWritten:])
  86. totalBytesRead += bytesRead
  87. totalBytesWritten += bytesWritten
  88. switch err {
  89. case syscall.E2BIG:
  90. // increase the size of the output buffer by another input length
  91. // first, create a new buffer
  92. tempBuffer := make([]byte, len(outputBuffer)+len(inputBuffer))
  93. // copy the existing data
  94. copy(tempBuffer, outputBuffer)
  95. // switch the buffers
  96. outputBuffer = tempBuffer
  97. // forget the error
  98. err = nil
  99. case syscall.EILSEQ, syscall.EINVAL:
  100. // iconv can still return these in cases where it still can proceed such as //IGNORE
  101. if bytesRead > 0 || bytesWritten > 0 {
  102. err = nil
  103. }
  104. }
  105. }
  106. if err == nil {
  107. // perform a final shift state reset
  108. _, bytesWritten, err = converter.Convert(nil, outputBuffer[totalBytesWritten:])
  109. // update total count
  110. totalBytesWritten += bytesWritten
  111. }
  112. // construct the final output string
  113. output = string(outputBuffer[:totalBytesWritten])
  114. return output, err
  115. }
  116. func finalizeConverter(converter *Converter) {
  117. converter.Close()
  118. }