From 2c039114d22b8a27d2bc8d96d870250627f0bb8d Mon Sep 17 00:00:00 2001 From: Tom Thorogood Date: Fri, 30 Nov 2018 06:27:48 +1030 Subject: [PATCH] Use a table lookup for escaping unprintable bytes (#846) --- msg.go | 5 +---- msg_helpers.go | 2 +- types.go | 42 +++++++++++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/msg.go b/msg.go index 46c2d9bf..1738ea4f 100644 --- a/msg.go +++ b/msg.go @@ -416,10 +416,7 @@ Loop: s = append(s, '\\', b) default: if b < ' ' || b > '~' { // unprintable, use \DDD - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s = append(s, '\\', '0', '0', '0') - copy(s[len(s)-len(bufs):], bufs) + s = append(s, escapeByte(b)...) } else { s = append(s, b) } diff --git a/msg_helpers.go b/msg_helpers.go index a5b342e3..fdc000f2 100644 --- a/msg_helpers.go +++ b/msg_helpers.go @@ -275,7 +275,7 @@ func unpackString(msg []byte, off int) (string, int, error) { s.WriteByte('\\') s.WriteByte(b) case b < ' ' || b > '~': // unprintable - writeEscapedByte(&s, b) + s.WriteString(escapeByte(b)) default: s.WriteByte(b) } diff --git a/types.go b/types.go index 115f2c7b..a1d85dbc 100644 --- a/types.go +++ b/types.go @@ -460,7 +460,7 @@ func sprintTxtOctet(s string) string { case b == '.': dst.WriteByte('.') case b < ' ' || b > '~': - writeEscapedByte(&dst, b) + dst.WriteString(escapeByte(b)) default: dst.WriteByte(b) } @@ -508,20 +508,44 @@ func writeTXTStringByte(s *strings.Builder, b byte) { s.WriteByte('\\') s.WriteByte(b) case b < ' ' || b > '~': - writeEscapedByte(s, b) + s.WriteString(escapeByte(b)) default: s.WriteByte(b) } } -func writeEscapedByte(s *strings.Builder, b byte) { - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s.WriteByte('\\') - for i := len(bufs); i < 3; i++ { - s.WriteByte('0') +const ( + escapedByteSmall = "" + + `\000\001\002\003\004\005\006\007\008\009` + + `\010\011\012\013\014\015\016\017\018\019` + + `\020\021\022\023\024\025\026\027\028\029` + + `\030\031` + escapedByteLarge = `\127\128\129` + + `\130\131\132\133\134\135\136\137\138\139` + + `\140\141\142\143\144\145\146\147\148\149` + + `\150\151\152\153\154\155\156\157\158\159` + + `\160\161\162\163\164\165\166\167\168\169` + + `\170\171\172\173\174\175\176\177\178\179` + + `\180\181\182\183\184\185\186\187\188\189` + + `\190\191\192\193\194\195\196\197\198\199` + + `\200\201\202\203\204\205\206\207\208\209` + + `\210\211\212\213\214\215\216\217\218\219` + + `\220\221\222\223\224\225\226\227\228\229` + + `\230\231\232\233\234\235\236\237\238\239` + + `\240\241\242\243\244\245\246\247\248\249` + + `\250\251\252\253\254\255` +) + +// escapeByte returns the \DDD escaping of b which must +// satisfy b < ' ' || b > '~'. +func escapeByte(b byte) string { + if b < ' ' { + return escapedByteSmall[b*4 : b*4+4] } - s.Write(bufs) + + b -= '~' + 1 + // The cast here is needed as b*4 may overflow byte. + return escapedByteLarge[int(b)*4 : int(b)*4+4] } func nextByte(s string, offset int) (byte, int) {