Skip to content

Commit d5dce15

Browse files
committed
fix
1 parent 432e128 commit d5dce15

File tree

4 files changed

+143
-181
lines changed

4 files changed

+143
-181
lines changed

modules/charset/charset.go

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ type ConvertOpts struct {
2626
KeepBOM bool
2727
}
2828

29+
var ToUTF8WithFallbackReaderPrefetchSize = 16 * 1024
30+
2931
// ToUTF8WithFallbackReader detects the encoding of content and converts to UTF-8 reader if possible
3032
func ToUTF8WithFallbackReader(rd io.Reader, opts ConvertOpts) io.Reader {
31-
buf := make([]byte, 2048)
33+
buf := make([]byte, ToUTF8WithFallbackReaderPrefetchSize)
3234
n, err := util.ReadAtMost(rd, buf)
3335
if err != nil {
3436
return io.MultiReader(bytes.NewReader(MaybeRemoveBOM(buf[:n], opts)), rd)
@@ -41,6 +43,7 @@ func ToUTF8WithFallbackReader(rd io.Reader, opts ConvertOpts) io.Reader {
4143

4244
encoding, _ := charset.Lookup(charsetLabel)
4345
if encoding == nil {
46+
log.Error("Unknown encoding: %s", charsetLabel)
4447
return io.MultiReader(bytes.NewReader(buf[:n]), rd)
4548
}
4649

@@ -54,17 +57,18 @@ func ToUTF8WithFallbackReader(rd io.Reader, opts ConvertOpts) io.Reader {
5457
}
5558

5659
// ToUTF8 converts content to UTF8 encoding
57-
func ToUTF8(content []byte, opts ConvertOpts) (string, error) {
60+
func ToUTF8(content []byte, opts ConvertOpts) ([]byte, error) {
5861
charsetLabel, err := DetectEncoding(content)
5962
if err != nil {
60-
return "", err
63+
return content, err
6164
} else if charsetLabel == "UTF-8" {
62-
return string(MaybeRemoveBOM(content, opts)), nil
65+
return MaybeRemoveBOM(content, opts), nil
6366
}
6467

6568
encoding, _ := charset.Lookup(charsetLabel)
6669
if encoding == nil {
67-
return string(content), fmt.Errorf("Unknown encoding: %s", charsetLabel)
70+
log.Error("Unknown encoding: %s", charsetLabel)
71+
return content, fmt.Errorf("unknown encoding: %s", charsetLabel)
6872
}
6973

7074
// If there is an error, we concatenate the nicely decoded part and the
@@ -76,7 +80,7 @@ func ToUTF8(content []byte, opts ConvertOpts) (string, error) {
7680

7781
result = MaybeRemoveBOM(result, opts)
7882

79-
return string(result), err
83+
return result, err
8084
}
8185

8286
// ToUTF8WithFallback detects the encoding of content and converts to UTF-8 if possible
@@ -94,6 +98,7 @@ func ToUTF8DropErrors(content []byte, opts ConvertOpts) []byte {
9498

9599
encoding, _ := charset.Lookup(charsetLabel)
96100
if encoding == nil {
101+
log.Error("Unknown encoding: %s", charsetLabel)
97102
return content
98103
}
99104

@@ -130,28 +135,37 @@ func MaybeRemoveBOM(content []byte, opts ConvertOpts) []byte {
130135
}
131136

132137
// DetectEncoding detect the encoding of content
133-
func DetectEncoding(content []byte) (string, error) {
138+
// it always returns a detected or guessed "encoding" string, no matter error happens or not
139+
func DetectEncoding(content []byte) (encoding string, _ error) {
134140
// First we check if the content represents valid utf8 content excepting a truncated character at the end.
135141

136142
// Now we could decode all the runes in turn but this is not necessarily the cheapest thing to do
137-
// instead we walk backwards from the end to trim off a the incomplete character
143+
// instead we walk backwards from the end to trim off the incomplete character
138144
toValidate := content
139145
end := len(toValidate) - 1
140146

141-
if end < 0 {
142-
// no-op
143-
} else if toValidate[end]>>5 == 0b110 {
144-
// Incomplete 1 byte extension e.g. © <c2><a9> which has been truncated to <c2>
145-
toValidate = toValidate[:end]
146-
} else if end > 0 && toValidate[end]>>6 == 0b10 && toValidate[end-1]>>4 == 0b1110 {
147-
// Incomplete 2 byte extension e.g. ⛔ <e2><9b><94> which has been truncated to <e2><9b>
148-
toValidate = toValidate[:end-1]
149-
} else if end > 1 && toValidate[end]>>6 == 0b10 && toValidate[end-1]>>6 == 0b10 && toValidate[end-2]>>3 == 0b11110 {
150-
// Incomplete 3 byte extension e.g. 💩 <f0><9f><92><a9> which has been truncated to <f0><9f><92>
151-
toValidate = toValidate[:end-2]
147+
// U+0000 U+007F 0yyyzzzz
148+
// U+0080 U+07FF 110xxxyy 10yyzzzz
149+
// U+0800 U+FFFF 1110wwww 10xxxxyy 10yyzzzz
150+
// U+010000 U+10FFFF 11110uvv 10vvwwww 10xxxxyy 10yyzzzz
151+
cnt := 0
152+
for end >= 0 && cnt < 4 {
153+
c := toValidate[end]
154+
if c>>5 == 0b110 || c>>4 == 0b1110 || c>>3 == 0b11110 {
155+
// a leading byte
156+
toValidate = toValidate[:end]
157+
break
158+
} else if c>>6 == 0b10 {
159+
// a continuation byte
160+
end--
161+
} else {
162+
// not an utf-8 byte
163+
break
164+
}
165+
cnt++
152166
}
167+
153168
if utf8.Valid(toValidate) {
154-
log.Debug("Detected encoding: utf-8 (fast)")
155169
return "UTF-8", nil
156170
}
157171

@@ -160,7 +174,7 @@ func DetectEncoding(content []byte) (string, error) {
160174
if len(content) < 1024 {
161175
// Check if original content is valid
162176
if _, err := textDetector.DetectBest(content); err != nil {
163-
return "", err
177+
return util.IfZero(setting.Repository.AnsiCharset, "UTF-8"), err
164178
}
165179
times := 1024 / len(content)
166180
detectContent = make([]byte, 0, times*len(content))
@@ -171,14 +185,10 @@ func DetectEncoding(content []byte) (string, error) {
171185
detectContent = content
172186
}
173187

174-
// Now we can't use DetectBest or just results[0] because the result isn't stable - so we need a tie break
188+
// Now we can't use DetectBest or just results[0] because the result isn't stable - so we need a tie-break
175189
results, err := textDetector.DetectAll(detectContent)
176190
if err != nil {
177-
if err == chardet.NotDetectedError && len(setting.Repository.AnsiCharset) > 0 {
178-
log.Debug("Using default AnsiCharset: %s", setting.Repository.AnsiCharset)
179-
return setting.Repository.AnsiCharset, nil
180-
}
181-
return "", err
191+
return util.IfZero(setting.Repository.AnsiCharset, "UTF-8"), err
182192
}
183193

184194
topConfidence := results[0].Confidence
@@ -201,11 +211,9 @@ func DetectEncoding(content []byte) (string, error) {
201211
}
202212

203213
// FIXME: to properly decouple this function the fallback ANSI charset should be passed as an argument
204-
if topResult.Charset != "UTF-8" && len(setting.Repository.AnsiCharset) > 0 {
205-
log.Debug("Using default AnsiCharset: %s", setting.Repository.AnsiCharset)
214+
if topResult.Charset != "UTF-8" && setting.Repository.AnsiCharset != "" {
206215
return setting.Repository.AnsiCharset, err
207216
}
208217

209-
log.Debug("Detected encoding: %s", topResult.Charset)
210-
return topResult.Charset, err
218+
return topResult.Charset, nil
211219
}

0 commit comments

Comments
 (0)