diff options
Diffstat (limited to 'vendor/github.com/tdewolff/minify/common.go')
-rw-r--r-- | vendor/github.com/tdewolff/minify/common.go | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/vendor/github.com/tdewolff/minify/common.go b/vendor/github.com/tdewolff/minify/common.go new file mode 100644 index 0000000..fa3c91d --- /dev/null +++ b/vendor/github.com/tdewolff/minify/common.go @@ -0,0 +1,339 @@ +package minify // import "github.com/tdewolff/minify" + +import ( + "bytes" + "encoding/base64" + "net/url" + + "github.com/tdewolff/parse" + "github.com/tdewolff/parse/strconv" +) + +// Epsilon is the closest number to zero that is not considered to be zero. +var Epsilon = 0.00001 + +// ContentType minifies a given mediatype by removing all whitespace. +func ContentType(b []byte) []byte { + j := 0 + start := 0 + inString := false + for i, c := range b { + if !inString && parse.IsWhitespace(c) { + if start != 0 { + j += copy(b[j:], b[start:i]) + } else { + j += i + } + start = i + 1 + } else if c == '"' { + inString = !inString + } + } + if start != 0 { + j += copy(b[j:], b[start:]) + return parse.ToLower(b[:j]) + } + return parse.ToLower(b) +} + +// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt. +func DataURI(m *M, dataURI []byte) []byte { + if mediatype, data, err := parse.DataURI(dataURI); err == nil { + dataURI, _ = m.Bytes(string(mediatype), data) + base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(dataURI)) + asciiLen := len(dataURI) + for _, c := range dataURI { + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' || c == '_' || c == '.' || c == '~' || c == ' ' { + asciiLen++ + } else { + asciiLen += 2 + } + if asciiLen > base64Len { + break + } + } + if asciiLen > base64Len { + encoded := make([]byte, base64Len-len(";base64")) + base64.StdEncoding.Encode(encoded, dataURI) + dataURI = encoded + mediatype = append(mediatype, []byte(";base64")...) + } else { + dataURI = []byte(url.QueryEscape(string(dataURI))) + dataURI = bytes.Replace(dataURI, []byte("\""), []byte("\\\""), -1) + } + if len("text/plain") <= len(mediatype) && parse.EqualFold(mediatype[:len("text/plain")], []byte("text/plain")) { + mediatype = mediatype[len("text/plain"):] + } + for i := 0; i+len(";charset=us-ascii") <= len(mediatype); i++ { + // must start with semicolon and be followed by end of mediatype or semicolon + if mediatype[i] == ';' && parse.EqualFold(mediatype[i+1:i+len(";charset=us-ascii")], []byte("charset=us-ascii")) && (i+len(";charset=us-ascii") >= len(mediatype) || mediatype[i+len(";charset=us-ascii")] == ';') { + mediatype = append(mediatype[:i], mediatype[i+len(";charset=us-ascii"):]...) + break + } + } + dataURI = append(append(append([]byte("data:"), mediatype...), ','), dataURI...) + } + return dataURI +} + +const MaxInt = int(^uint(0) >> 1) +const MinInt = -MaxInt - 1 + +// Number minifies a given byte slice containing a number (see parse.Number) and removes superfluous characters. +func Number(num []byte, prec int) []byte { + // omit first + and register mantissa start and end, whether it's negative and the exponent + neg := false + start := 0 + dot := -1 + end := len(num) + origExp := 0 + if 0 < end && (num[0] == '+' || num[0] == '-') { + if num[0] == '-' { + neg = true + } + start++ + } + for i, c := range num[start:] { + if c == '.' { + dot = start + i + } else if c == 'e' || c == 'E' { + end = start + i + i += start + 1 + if i < len(num) && num[i] == '+' { + i++ + } + if tmpOrigExp, n := strconv.ParseInt(num[i:]); n > 0 && tmpOrigExp >= int64(MinInt) && tmpOrigExp <= int64(MaxInt) { + // range checks for when int is 32 bit + origExp = int(tmpOrigExp) + } else { + return num + } + break + } + } + if dot == -1 { + dot = end + } + + // trim leading zeros but leave at least one digit + for start < end-1 && num[start] == '0' { + start++ + } + // trim trailing zeros + i := end - 1 + for ; i > dot; i-- { + if num[i] != '0' { + end = i + 1 + break + } + } + if i == dot { + end = dot + if start == end { + num[start] = '0' + return num[start : start+1] + } + } else if start == end-1 && num[start] == '0' { + return num[start:end] + } + + // n is the number of significant digits + // normExp would be the exponent if it were normalised (0.1 <= f < 1) + n := 0 + normExp := 0 + if dot == start { + for i = dot + 1; i < end; i++ { + if num[i] != '0' { + n = end - i + normExp = dot - i + 1 + break + } + } + } else if dot == end { + normExp = end - start + for i = end - 1; i >= start; i-- { + if num[i] != '0' { + n = i + 1 - start + end = i + 1 + break + } + } + } else { + n = end - start - 1 + normExp = dot - start + } + + if origExp < 0 && (normExp < MinInt-origExp || normExp-n < MinInt-origExp) || origExp > 0 && (normExp > MaxInt-origExp || normExp-n > MaxInt-origExp) { + return num + } + normExp += origExp + + // intExp would be the exponent if it were an integer + intExp := normExp - n + lenIntExp := 1 + if intExp <= -10 || intExp >= 10 { + lenIntExp = strconv.LenInt(int64(intExp)) + } + + // there are three cases to consider when printing the number + // case 1: without decimals and with an exponent (large numbers) + // case 2: with decimals and without an exponent (around zero) + // case 3: without decimals and with a negative exponent (small numbers) + if normExp >= n { + // case 1 + if dot < end { + if dot == start { + start = end - n + } else { + // TODO: copy the other part if shorter? + copy(num[dot:], num[dot+1:end]) + end-- + } + } + if normExp >= n+3 { + num[end] = 'e' + end++ + for i := end + lenIntExp - 1; i >= end; i-- { + num[i] = byte(intExp%10) + '0' + intExp /= 10 + } + end += lenIntExp + } else if normExp == n+2 { + num[end] = '0' + num[end+1] = '0' + end += 2 + } else if normExp == n+1 { + num[end] = '0' + end++ + } + } else if normExp >= -lenIntExp-1 { + // case 2 + zeroes := -normExp + newDot := 0 + if zeroes > 0 { + // dot placed at the front and add zeroes + newDot = end - n - zeroes - 1 + if newDot != dot { + d := start - newDot + if d > 0 { + if dot < end { + // copy original digits behind the dot backwards + copy(num[dot+1+d:], num[dot+1:end]) + if dot > start { + // copy original digits before the dot backwards + copy(num[start+d+1:], num[start:dot]) + } + } else if dot > start { + // copy original digits before the dot backwards + copy(num[start+d:], num[start:dot]) + } + newDot = start + end += d + } else { + start += -d + } + num[newDot] = '.' + for i := 0; i < zeroes; i++ { + num[newDot+1+i] = '0' + } + } + } else { + // placed in the middle + if dot == start { + // TODO: try if placing at the end reduces copying + // when there are zeroes after the dot + dot = end - n - 1 + start = dot + } else if dot >= end { + // TODO: try if placing at the start reduces copying + // when input has no dot in it + dot = end + end++ + } + newDot = start + normExp + if newDot > dot { + // copy digits forwards + copy(num[dot:], num[dot+1:newDot+1]) + } else if newDot < dot { + // copy digits backwards + copy(num[newDot+1:], num[newDot:dot]) + } + num[newDot] = '.' + } + + // apply precision + dot = newDot + if prec > -1 && dot+1+prec < end { + end = dot + 1 + prec + inc := num[end] >= '5' + if inc || num[end-1] == '0' { + for i := end - 1; i > start; i-- { + if i == dot { + end-- + } else if inc { + if num[i] == '9' { + if i > dot { + end-- + } else { + num[i] = '0' + } + } else { + num[i]++ + inc = false + break + } + } else if i > dot && num[i] == '0' { + end-- + } + } + } + if dot == start && end == start+1 { + if inc { + num[start] = '1' + } else { + num[start] = '0' + } + } else { + if dot+1 == end { + end-- + } + if inc { + if num[start] == '9' { + num[start] = '0' + copy(num[start+1:], num[start:end]) + end++ + num[start] = '1' + } else { + num[start]++ + } + } + } + } + } else { + // case 3 + if dot < end { + if dot == start { + copy(num[start:], num[end-n:end]) + end = start + n + } else { + copy(num[dot:], num[dot+1:end]) + end-- + } + } + num[end] = 'e' + num[end+1] = '-' + end += 2 + intExp = -intExp + for i := end + lenIntExp - 1; i >= end; i-- { + num[i] = byte(intExp%10) + '0' + intExp /= 10 + } + end += lenIntExp + } + + if neg { + start-- + num[start] = '-' + } + return num[start:end] +} |