...

Source file src/github.com/chenzhuoyu/base64x/base64x_test.go

Documentation: github.com/chenzhuoyu/base64x

     1  package base64x
     2  
     3  import (
     4      `crypto/rand`
     5      `encoding/base64`
     6      `io`
     7      `reflect`
     8      `strings`
     9      `testing`
    10      `unsafe`
    11  )
    12  
    13  type TestPair struct {
    14      decoded string
    15      encoded string
    16  }
    17  
    18  type EncodingTest struct {
    19      enc  Encoding            // Encoding to test
    20      conv func(string) string // Reference string converter
    21  }
    22  
    23  var pairs = []TestPair{
    24      // RFC 3548 examples
    25      {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"},
    26      {"\x14\xfb\x9c\x03\xd9", "FPucA9k="},
    27      {"\x14\xfb\x9c\x03", "FPucAw=="},
    28  
    29      // RFC 4648 examples
    30      {"", ""},
    31      {"f", "Zg=="},
    32      {"fo", "Zm8="},
    33      {"foo", "Zm9v"},
    34      {"foob", "Zm9vYg=="},
    35      {"fooba", "Zm9vYmE="},
    36      {"foobar", "Zm9vYmFy"},
    37  
    38      // Wikipedia examples
    39      {"sure.", "c3VyZS4="},
    40      {"sure", "c3VyZQ=="},
    41      {"sur", "c3Vy"},
    42      {"su", "c3U="},
    43      {"leasure.", "bGVhc3VyZS4="},
    44      {"easure.", "ZWFzdXJlLg=="},
    45      {"asure.", "YXN1cmUu"},
    46      {"sure.", "c3VyZS4="},
    47  
    48      // Relatively long strings
    49      {
    50          "Twas brillig, and the slithy toves",
    51          "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
    52      }, {
    53          "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
    54          "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
    55          "nXlI0lmeXmWesZqaEv6KB8cHzOhsgfLZ44m1mO69jnRRYDI+XHQ6X9d35rWWx/+c",
    56      },
    57  }
    58  
    59  var crlf_pairs = []TestPair{
    60      // RFC 3548 examples
    61      {"\x14\xfb\x9c\x03\xd9\x7e", "FPuc\r\nA9l+"},
    62      {"\x14\xfb\x9c\x03\xd9", "FP\r\r\r\rucA9k="},
    63      {"\x14\xfb\x9c\x03", "\r\nFPucAw=\r=\n"},
    64  
    65      // RFC 4648 examples
    66      {"", "\r"},
    67      {"f", "Zg\r\n=="},
    68      {"fo", "Zm\r\n8="},
    69      {"fooba", "Zm\r\n9vY\r\nmE="},
    70  
    71      // Wikipedia examples
    72      {"su", "c3U\r="},
    73      {"leasure.", "bGVhc3VyZ\nS4="},
    74      {"easure.", "ZW\r\nFzdXJlLg=\r=\r\n"},
    75      {"asure.", "YXN1cmUu"},
    76      {"sure.", "c3VyZ\r\nS4="},
    77  
    78      // Relatively long strings
    79      {
    80          "Twas brillig, and the slithy toves",
    81          "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw\r\n==\r\n",
    82      }, {
    83          "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
    84          "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
    85          "nXlI0lmeXmWesZqaEv6KB8cHzOhsg\r\nfLZ44m1mO69jnRRYDI+XH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nQ6X9d35rWWx/\r\n+c",
    86      },
    87  }
    88  
    89  var json_pairs = []TestPair{
    90      // RFC 3548 examples
    91      {"\x14\xfb\x9c\x03\xd9\x7e", `FPu\rcA9l+\n`},
    92      {"\x14\xfb\x9c\x03\xd9\x7e", `FPuc\u00419l+`},
    93      {"\x14\xfb\x9c\x03\xd9", `FPucA9k\u003d`},
    94      {"\x14\xfb\x9c\x03\xd9", `FPucA\u0039k\u003d`},
    95      {"\x14\xfb\x9c\x03", `FPucAw\u003d\u003d`},
    96  
    97      // RFC 4648 examples
    98      {"", ""},
    99      {"f", "Zg=="},
   100      {"fo", "Zm8="},
   101      {"foo", "Zm9v"},
   102      {"foob", "Zm9vYg=="},
   103      {"fooba", "Zm9vYmE="},
   104      {"foobar", "Zm9vYmFy"},
   105  
   106      // Wikipedia examples
   107      {"sure.", "c3VyZS4="},
   108      {"sure", "c3VyZQ=="},
   109      {"sur", "c3Vy"},
   110      {"su", "c3U="},
   111      {"leasure.", "bGVhc3VyZS4="},
   112      {"easure.", "ZWFzdXJlLg=="},
   113      {"asure.", "YXN1cmUu"},
   114      {"sure.", "c3VyZS4="},
   115  
   116      // Relatively long strings
   117      {
   118          "Twas brillig, and the slithy toves",
   119          "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
   120      }, {
   121          "\x9dyH\xd2Y\x9e^e\x9e\xb1\x9a\x9a\x12\xfe\x8a\x07\xc7\x07\xcc\xe8l\x81" +
   122          "\xf2\xd9\xe3\x89\xb5\x98\xee\xbd\x8etQ`2>\\t:_\xd7w\xe6\xb5\x96\xc7\xff\x9c",
   123          `nXlI0lmeXmWesZqaEv6KB8cHzOhsgfLZ44m1mO\u0036\u0039jnRRYDI+XHQ6X9d35rWWx\/+c`,
   124      },
   125  }
   126  
   127  // Do nothing to a reference base64 string (leave in standard format)
   128  func stdRef(ref string) string {
   129      return ref
   130  }
   131  
   132  // Convert a reference string to URL-encoding
   133  func urlRef(ref string) string {
   134      ref = strings.ReplaceAll(ref, "+", "-")
   135      ref = strings.ReplaceAll(ref, "/", "_")
   136      return ref
   137  }
   138  
   139  // Convert a reference string to raw, unpadded format
   140  func rawRef(ref string) string {
   141      return strings.ReplaceAll(ref, "=", "")
   142  }
   143  
   144  // Both URL and unpadding conversions
   145  func rawURLRef(ref string) string {
   146      return rawRef(urlRef(ref))
   147  }
   148  
   149  var encodingTests = []EncodingTest{
   150      {StdEncoding, stdRef},
   151      {URLEncoding, urlRef},
   152      {RawStdEncoding, rawRef},
   153      {RawURLEncoding, rawURLRef},
   154  }
   155  
   156  func testEqual(t *testing.T, msg string, args ...interface{}) bool {
   157      t.Helper()
   158      if args[len(args) - 2] != args[len(args) - 1] {
   159          t.Errorf(msg, args...)
   160          return false
   161      }
   162      return true
   163  }
   164  
   165  func TestEncoderRecover(t *testing.T) {
   166      t.Run("nil dst", func(t *testing.T) {
   167          in := []byte("abc")
   168          defer func(){
   169              if v := recover(); v != nil {
   170                  println("recover:", v)
   171              } else {
   172                  t.Fatal("not recover")
   173              }
   174          }()
   175          b64encode(nil, &in, int(StdEncoding))
   176      })
   177      t.Run("nil src", func(t *testing.T) {
   178          in := []byte("abc")
   179          (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data = uintptr(0)
   180          out := make([]byte, 0, 10)
   181          defer func(){
   182              if v := recover(); v != nil {
   183                  println("recover:", v)
   184              } else {
   185                  t.Fatal("not recover")
   186              }
   187          }()
   188          b64encode(&out, &in, int(StdEncoding))
   189      })
   190  }
   191  
   192  func TestEncoder(t *testing.T) {
   193      for _, p := range pairs {
   194          for _, tt := range encodingTests {
   195              got := tt.enc.EncodeToString([]byte(p.decoded))
   196              testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, tt.conv(p.encoded))
   197          }
   198      }
   199  }
   200  
   201  func benchmarkStdlibWithSize(b *testing.B, nb int) {
   202      buf := make([]byte, nb)
   203      dst := make([]byte, base64.StdEncoding.EncodedLen(nb))
   204      _, _ = io.ReadFull(rand.Reader, buf)
   205      b.SetBytes(int64(nb))
   206      b.ResetTimer()
   207      b.RunParallel(func(pb *testing.PB) {
   208          for pb.Next() {
   209              base64.StdEncoding.Encode(dst, buf)
   210          }
   211      })
   212  }
   213  
   214  func benchmarkBase64xWithSize(b *testing.B, nb int) {
   215      buf := make([]byte, nb)
   216      dst := make([]byte, StdEncoding.EncodedLen(nb))
   217      _, _ = io.ReadFull(rand.Reader, buf)
   218      b.SetBytes(int64(nb))
   219      b.ResetTimer()
   220      b.RunParallel(func(pb *testing.PB) {
   221          for pb.Next() {
   222              StdEncoding.Encode(dst, buf)
   223          }
   224      })
   225  }
   226  
   227  func BenchmarkEncoderStdlib_16B    (b *testing.B) { benchmarkStdlibWithSize(b, 16) }
   228  func BenchmarkEncoderStdlib_56B    (b *testing.B) { benchmarkStdlibWithSize(b, 56) }
   229  func BenchmarkEncoderStdlib_128B   (b *testing.B) { benchmarkStdlibWithSize(b, 128) }
   230  func BenchmarkEncoderStdlib_4kB    (b *testing.B) { benchmarkStdlibWithSize(b, 4 * 1024) }
   231  func BenchmarkEncoderStdlib_256kB  (b *testing.B) { benchmarkStdlibWithSize(b, 256 * 1024) }
   232  func BenchmarkEncoderStdlib_1MB    (b *testing.B) { benchmarkStdlibWithSize(b, 1024 * 1024) }
   233  
   234  func BenchmarkEncoderBase64x_16B   (b *testing.B) { benchmarkBase64xWithSize(b, 16) }
   235  func BenchmarkEncoderBase64x_56B   (b *testing.B) { benchmarkBase64xWithSize(b, 56) }
   236  func BenchmarkEncoderBase64x_128B  (b *testing.B) { benchmarkBase64xWithSize(b, 128) }
   237  func BenchmarkEncoderBase64x_4kB   (b *testing.B) { benchmarkBase64xWithSize(b, 4 * 1024) }
   238  func BenchmarkEncoderBase64x_256kB (b *testing.B) { benchmarkBase64xWithSize(b, 256 * 1024) }
   239  func BenchmarkEncoderBase64x_1MB   (b *testing.B) { benchmarkBase64xWithSize(b, 1024 * 1024) }
   240  
   241  func TestDecoder(t *testing.T) {
   242      for _, p := range pairs {
   243          for _, tt := range encodingTests {
   244              encoded := tt.conv(p.encoded)
   245              dbuf := make([]byte, tt.enc.DecodedLen(len(encoded)))
   246              count, err := tt.enc.Decode(dbuf, []byte(encoded))
   247              testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
   248              testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
   249              testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
   250  
   251              dbuf, err = tt.enc.DecodeString(encoded)
   252              testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
   253              testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
   254          }
   255      }
   256  }
   257  
   258  func TestDecoderRecover(t *testing.T) {
   259      t.Run("nil dst", func(t *testing.T) {
   260          in := []byte("abc")
   261          defer func(){
   262              if v := recover(); v != nil {
   263                  println("recover:", v)
   264              } else {
   265                  t.Fatal("not recover")
   266              }
   267          }()
   268          b64decode(nil, unsafe.Pointer(&in[0]), len(in), int(StdEncoding))
   269      })
   270      t.Run("nil src", func(t *testing.T) {
   271          out := make([]byte, 0, 10)
   272          defer func(){
   273              if v := recover(); v != nil {
   274                  println("recover:", v)
   275              } else {
   276                  t.Fatal("not recover")
   277              }
   278          }()
   279          b64decode(&out, nil, 5, int(StdEncoding))
   280      })
   281  }
   282  
   283  func TestDecoderCRLF(t *testing.T) {
   284      for _, p := range crlf_pairs {
   285          for _, tt := range encodingTests {
   286              encoded := tt.conv(p.encoded)
   287              dbuf := make([]byte, tt.enc.DecodedLen(len(encoded)))
   288              count, err := tt.enc.Decode(dbuf, []byte(encoded))
   289              testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
   290              testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
   291              testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
   292  
   293              dbuf, err = tt.enc.DecodeString(encoded)
   294              testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
   295              testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
   296          }
   297      }
   298  }
   299  
   300  func TestDecoderJSON(t *testing.T) {
   301      for _, p := range json_pairs {
   302          encoded := p.encoded
   303          dbuf := make([]byte, JSONStdEncoding.DecodedLen(len(encoded)))
   304          count, err := JSONStdEncoding.Decode(dbuf, []byte(encoded))
   305          testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil))
   306          testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded))
   307          testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded)
   308  
   309          dbuf, err = JSONStdEncoding.DecodeString(encoded)
   310          testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil))
   311          testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded)
   312      }
   313  }
   314  
   315  func TestDecoderError(t *testing.T) {
   316      _, err := StdEncoding.DecodeString("!aGVsbG8sIHdvcmxk")
   317      if err != base64.CorruptInputError(0) {
   318          panic(err)
   319      }
   320      _, err = StdEncoding.DecodeString("aGVsbG8!sIHdvcmxk")
   321      if err != base64.CorruptInputError(7) {
   322          panic(err)
   323      }
   324      _, err = StdEncoding.DecodeString("123456")
   325      if err != base64.CorruptInputError(6) {
   326          panic(err)
   327      }
   328      _, err = StdEncoding.DecodeString("1234;6")
   329      if err != base64.CorruptInputError(4) {
   330          panic(err)
   331      }
   332      _, err = StdEncoding.DecodeString("F\xaa\xaa\xaa\xaaDDDDDDDDDDDDD//z")
   333      if err != base64.CorruptInputError(1) {
   334          panic(err)
   335      } 
   336  }
   337  
   338  func benchmarkStdlibDecoder(b *testing.B, v string) {
   339      src := []byte(v)
   340      dst := make([]byte, base64.StdEncoding.DecodedLen(len(v)))
   341      b.SetBytes(int64(len(v)))
   342      b.ResetTimer()
   343      b.RunParallel(func(pb *testing.PB) {
   344          for pb.Next() {
   345              _, _ = base64.StdEncoding.Decode(dst, src)
   346          }
   347      })
   348  }
   349  
   350  func benchmarkBase64xDecoder(b *testing.B, v string) {
   351      src := []byte(v)
   352      dst := make([]byte, StdEncoding.DecodedLen(len(v)))
   353      b.SetBytes(int64(len(v)))
   354      b.ResetTimer()
   355      b.RunParallel(func(pb *testing.PB) {
   356          for pb.Next() {
   357              _, _ = StdEncoding.Decode(dst, src)
   358          }
   359      })
   360  }
   361  
   362  var data = `////////////////////////////////////////////////////////////////`
   363  func BenchmarkDecoderStdLib  (b *testing.B) { benchmarkStdlibDecoder(b, data) }
   364  func BenchmarkDecoderBase64x (b *testing.B) { benchmarkBase64xDecoder(b, data) }
   365  

View as plain text