Skip to content

Commit c2b6332

Browse files
committed
fix
1 parent 432e128 commit c2b6332

File tree

4 files changed

+127
-181
lines changed

4 files changed

+127
-181
lines changed

modules/charset/charset.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"strings"
1111
"unicode/utf8"
1212

13-
"code.gitea.io/gitea/modules/log"
1413
"code.gitea.io/gitea/modules/setting"
1514
"code.gitea.io/gitea/modules/util"
1615

@@ -26,9 +25,11 @@ type ConvertOpts struct {
2625
KeepBOM bool
2726
}
2827

28+
var ToUTF8WithFallbackReaderPrefetchSize = 16 * 1024
29+
2930
// ToUTF8WithFallbackReader detects the encoding of content and converts to UTF-8 reader if possible
3031
func ToUTF8WithFallbackReader(rd io.Reader, opts ConvertOpts) io.Reader {
31-
buf := make([]byte, 2048)
32+
buf := make([]byte, ToUTF8WithFallbackReaderPrefetchSize)
3233
n, err := util.ReadAtMost(rd, buf)
3334
if err != nil {
3435
return io.MultiReader(bytes.NewReader(MaybeRemoveBOM(buf[:n], opts)), rd)
@@ -54,17 +55,17 @@ func ToUTF8WithFallbackReader(rd io.Reader, opts ConvertOpts) io.Reader {
5455
}
5556

5657
// ToUTF8 converts content to UTF8 encoding
57-
func ToUTF8(content []byte, opts ConvertOpts) (string, error) {
58+
func ToUTF8(content []byte, opts ConvertOpts) ([]byte, error) {
5859
charsetLabel, err := DetectEncoding(content)
5960
if err != nil {
60-
return "", err
61+
return content, err
6162
} else if charsetLabel == "UTF-8" {
62-
return string(MaybeRemoveBOM(content, opts)), nil
63+
return MaybeRemoveBOM(content, opts), nil
6364
}
6465

6566
encoding, _ := charset.Lookup(charsetLabel)
6667
if encoding == nil {
67-
return string(content), fmt.Errorf("Unknown encoding: %s", charsetLabel)
68+
return content, fmt.Errorf("unknown encoding: %s", charsetLabel)
6869
}
6970

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

7778
result = MaybeRemoveBOM(result, opts)
7879

79-
return string(result), err
80+
return result, err
8081
}
8182

8283
// ToUTF8WithFallback detects the encoding of content and converts to UTF-8 if possible
@@ -130,28 +131,33 @@ func MaybeRemoveBOM(content []byte, opts ConvertOpts) []byte {
130131
}
131132

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

136138
// 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
139+
// instead we walk backwards from the end to trim off the incomplete character
138140
toValidate := content
139141
end := len(toValidate) - 1
140142

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]
143+
// U+0000 U+007F 0yyyzzzz
144+
// U+0080 U+07FF 110xxxyy 10yyzzzz
145+
// U+0800 U+FFFF 1110wwww 10xxxxyy 10yyzzzz
146+
// U+010000 U+10FFFF 11110uvv 10vvwwww 10xxxxyy 10yyzzzz
147+
cnt := 0
148+
for end >= 0 && cnt < 4 {
149+
c := toValidate[end]
150+
if c>>6 == 0b10 {
151+
end--
152+
}
153+
if c>>5 == 0b110 || c>>4 == 0b1110 || c>>3 == 0b11110 {
154+
toValidate = toValidate[:end]
155+
break
156+
}
157+
cnt++
152158
}
159+
153160
if utf8.Valid(toValidate) {
154-
log.Debug("Detected encoding: utf-8 (fast)")
155161
return "UTF-8", nil
156162
}
157163

@@ -160,7 +166,7 @@ func DetectEncoding(content []byte) (string, error) {
160166
if len(content) < 1024 {
161167
// Check if original content is valid
162168
if _, err := textDetector.DetectBest(content); err != nil {
163-
return "", err
169+
return util.IfZero(setting.Repository.AnsiCharset, "UTF-8"), err
164170
}
165171
times := 1024 / len(content)
166172
detectContent = make([]byte, 0, times*len(content))
@@ -171,14 +177,10 @@ func DetectEncoding(content []byte) (string, error) {
171177
detectContent = content
172178
}
173179

174-
// Now we can't use DetectBest or just results[0] because the result isn't stable - so we need a tie break
180+
// Now we can't use DetectBest or just results[0] because the result isn't stable - so we need a tie-break
175181
results, err := textDetector.DetectAll(detectContent)
176182
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
183+
return util.IfZero(setting.Repository.AnsiCharset, "UTF-8"), err
182184
}
183185

184186
topConfidence := results[0].Confidence
@@ -201,11 +203,9 @@ func DetectEncoding(content []byte) (string, error) {
201203
}
202204

203205
// 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)
206+
if topResult.Charset != "UTF-8" && setting.Repository.AnsiCharset != "" {
206207
return setting.Repository.AnsiCharset, err
207208
}
208209

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

0 commit comments

Comments
 (0)