From 6a2254e918944e5ead4d5a4d6d1b95b0e971c4b7 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sun, 21 Feb 2021 01:40:01 +0000 Subject: [PATCH] feat(performance): improve core undenting performance by around 20-30x Previously we relied heavily on regexp to filter out and grab all indentation white space, and then to strip away indentation shared across all lines. This was reasonably fast. However I wanted to see if I could make it faster by manually iterating over the input. Turns out doing so makes is around 20 times faster. The code is a lot more complicated though, but I'll attempt to break it down. There's three main phases to it: 1. Iterate over every character of the input to locate all line-feed (\n) characters, storing their indexes in a integer slice. 2. Iterate over the list of life-feed indexes, and for each line-feed, scan forward until a non-whitespace character is found, counting how many whitespace characters we encountered directly after the life-feed. If the number is lower than our previously lowest number of leading whitespace characters, store that as the new lowest number. 3. Now that we know the lowest number of leading whitespace characters common across every line of the input, we can iterate over the list of life-feed indexes again. This time to build the final output, but reading all characters from the life-feed index + whitespace number, until the next life-feed character, or end of input. Overall this approach yields a 15-20x speed improvement over the old method. Benchmarks, before: goos: darwin goarch: amd64 pkg: github.com/jimeh/undent cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz BenchmarkBytes/empty-8 78280611 15.18 ns/op BenchmarkBytes/single-line-8 2361297 515.1 ns/op BenchmarkBytes/single-line_indented-8 317440 3618 ns/op BenchmarkBytes/multi-line-8 630370 1920 ns/op BenchmarkBytes/multi-line_space_indented-8 156266 7664 ns/op BenchmarkBytes/multi-line_space_indented_without_any_leading_line-breaks-8 155672 8168 ns/op BenchmarkBytes/multi-line_space_indented_with_leading_line-breaks-8 144655 8165 ns/op BenchmarkBytes/multi-line_tab_indented-8 206425 5462 ns/op BenchmarkBytes/multi-line_tab_indented_without_any_leading_line-breaks-8 223620 5542 ns/op BenchmarkBytes/multi-line_tab_indented_with_leading_line-breaks-8 208132 5857 ns/op BenchmarkBytes/multi-line_tab_indented_with_tabs_and_spaces_after_indent-8 199480 5687 ns/op BenchmarkBytes/multi-line_space_indented_with_blank_lines-8 148402 8072 ns/op BenchmarkBytes/multi-line_tab_indented_with_blank_lines-8 200929 5691 ns/op BenchmarkBytes/multi-line_space_indented_with_random_indentation-8 197412 6515 ns/op BenchmarkBytes/multi-line_tab_indented_with_random_indentation-8 281493 4272 ns/op BenchmarkBytes/long_block_of_text-8 9894 115752 ns/op BenchmarkString/empty-8 100000000 12.75 ns/op BenchmarkString/single-line-8 2224165 529.0 ns/op BenchmarkString/single-line_indented-8 314088 3784 ns/op BenchmarkString/multi-line-8 645804 1968 ns/op BenchmarkString/multi-line_space_indented-8 149310 8103 ns/op BenchmarkString/multi-line_space_indented_without_any_leading_line-breaks-8 145390 8496 ns/op BenchmarkString/multi-line_space_indented_with_leading_line-breaks-8 145579 8161 ns/op BenchmarkString/multi-line_tab_indented-8 223596 5487 ns/op BenchmarkString/multi-line_tab_indented_without_any_leading_line-breaks-8 214842 5641 ns/op BenchmarkString/multi-line_tab_indented_with_leading_line-breaks-8 209067 5685 ns/op BenchmarkString/multi-line_tab_indented_with_tabs_and_spaces_after_indent-8 210307 5584 ns/op BenchmarkString/multi-line_space_indented_with_blank_lines-8 133948 9280 ns/op BenchmarkString/multi-line_tab_indented_with_blank_lines-8 178296 5769 ns/op BenchmarkString/multi-line_space_indented_with_random_indentation-8 206030 6222 ns/op BenchmarkString/multi-line_tab_indented_with_random_indentation-8 236450 4259 ns/op BenchmarkString/long_block_of_text-8 10000 113065 ns/op PASS ok github.com/jimeh/undent 44.800s Benchmarks, after: goos: darwin goarch: amd64 pkg: github.com/jimeh/undent cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz BenchmarkBytes/empty-8 596493562 2.074 ns/op BenchmarkBytes/single-line-8 20044598 60.64 ns/op BenchmarkBytes/single-line_indented-8 12449749 84.43 ns/op BenchmarkBytes/multi-line-8 5086376 232.3 ns/op BenchmarkBytes/multi-line_space_indented-8 3077774 400.4 ns/op BenchmarkBytes/multi-line_space_indented_without_any_leading_line-breaks-8 3011881 386.6 ns/op BenchmarkBytes/multi-line_space_indented_with_leading_line-breaks-8 3034299 402.9 ns/op BenchmarkBytes/multi-line_tab_indented-8 4500271 266.2 ns/op BenchmarkBytes/multi-line_tab_indented_without_any_leading_line-breaks-8 4355886 277.5 ns/op BenchmarkBytes/multi-line_tab_indented_with_leading_line-breaks-8 3758012 289.5 ns/op BenchmarkBytes/multi-line_tab_indented_with_tabs_and_spaces_after_indent-8 4425787 271.9 ns/op BenchmarkBytes/multi-line_space_indented_with_blank_lines-8 3035809 412.2 ns/op BenchmarkBytes/multi-line_tab_indented_with_blank_lines-8 3771512 334.2 ns/op BenchmarkBytes/multi-line_space_indented_with_random_indentation-8 4461404 275.6 ns/op BenchmarkBytes/multi-line_tab_indented_with_random_indentation-8 6960343 174.6 ns/op BenchmarkBytes/long_block_of_text-8 315788 3776 ns/op BenchmarkString/empty-8 338024905 3.761 ns/op BenchmarkString/single-line-8 20067831 59.28 ns/op BenchmarkString/single-line_indented-8 13826002 88.16 ns/op BenchmarkString/multi-line-8 4451938 261.6 ns/op BenchmarkString/multi-line_space_indented-8 2911797 411.1 ns/op BenchmarkString/multi-line_space_indented_without_any_leading_line-breaks-8 2699631 416.5 ns/op BenchmarkString/multi-line_space_indented_with_leading_line-breaks-8 2737174 436.3 ns/op BenchmarkString/multi-line_tab_indented-8 4208000 304.6 ns/op BenchmarkString/multi-line_tab_indented_without_any_leading_line-breaks-8 4029422 295.8 ns/op BenchmarkString/multi-line_tab_indented_with_leading_line-breaks-8 3929960 310.3 ns/op BenchmarkString/multi-line_tab_indented_with_tabs_and_spaces_after_indent-8 3978992 292.5 ns/op BenchmarkString/multi-line_space_indented_with_blank_lines-8 2829766 428.5 ns/op BenchmarkString/multi-line_tab_indented_with_blank_lines-8 3788185 304.8 ns/op BenchmarkString/multi-line_space_indented_with_random_indentation-8 4104337 279.4 ns/op BenchmarkString/multi-line_tab_indented_with_random_indentation-8 7092417 177.4 ns/op BenchmarkString/long_block_of_text-8 283140 4398 ns/op PASS ok github.com/jimeh/undent 47.252s --- undent.go | 105 +++++++++++++------ undent_test.go | 269 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 342 insertions(+), 32 deletions(-) diff --git a/undent.go b/undent.go index 7d040d0..dca5acb 100644 --- a/undent.go +++ b/undent.go @@ -5,15 +5,85 @@ package undent import ( "fmt" "io" - "regexp" ) -var matcher = regexp.MustCompile(`(?m)^([ \t]*)(?:\S)`) +const ( + tab = 9 + lf = 10 + spc = 32 +) // Bytes removes leading indentation/white-space from given string and returns // it as a byte slice. func Bytes(s string) []byte { - return []byte(String(s)) + if len(s) == 0 { + return []byte{} + } + + // find line feeds + lfs := []int{} + + if s[0] != lf { + lfs = append(lfs, -1) + } + + for i := 0; i < len(s); i++ { + if s[i] == lf { + lfs = append(lfs, i) + } + } + + // find smallest indent relative to each line-feed + min := 99999999999 + count := 0 + + for i := 0; i < len(lfs); i++ { + offset := lfs[i] + end := len(s) - 1 + if i+1 < len(lfs) { + end = lfs[i+1] + } + + if offset+1 >= end { + continue + } + + indent := 0 + lineSeek: + for n := offset + 1; n < end && indent < min; n++ { + switch s[n] { + case spc, tab: + indent++ + default: + break lineSeek + } + } + if indent < min { + min = indent + } + if indent > 0 { + count++ + } + } + + // extract each line without indentation + out := make([]byte, 0, len(s)-(min*count)) + + for i := 0; i < len(lfs); i++ { + offset := lfs[i] + 1 + end := len(s) + if i+1 < len(lfs) { + end = lfs[i+1] + 1 + } + + if offset+min < end { + out = append(out, s[offset+min:end]...) + } else if offset < end { + out = append(out, s[offset:end]...) + } + } + + return out } // Bytesf removes leading indentation/white-space from given format string @@ -25,34 +95,7 @@ func Bytesf(format string, a ...interface{}) []byte { // String removes leading indentation/white-space from given string. func String(s string) string { - if len(s) > 0 && s[0] == '\n' { - s = s[1:] - } - - matches := matcher.FindAllString(s, -1) - if len(matches) == 0 { - return s - } - - index := 0 - length := len(matches[0]) - - for i, s := range matches[1:] { - l := len(s) - if l < length { - index = i + 1 - length = l - } - } - - if length <= 1 { - return s - } - indent := matches[index][0 : length-1] - - return regexp.MustCompile( - `(?m)^`+regexp.QuoteMeta(indent), - ).ReplaceAllLiteralString(s, "") + return string(Bytes(s)) } // Stringf removes leading indentation/white-space from given format string diff --git a/undent_test.go b/undent_test.go index 7d1b10b..4de9cce 100644 --- a/undent_test.go +++ b/undent_test.go @@ -59,6 +59,21 @@ var stringTestCases = []struct { "foo": [ "bar" ] +}`, + }, + { + name: "multi-line space indented without any leading line-breaks", + s: ` { + "hello": "world", + "foo": [ + "bar" + ] + }`, + want: `{ + "hello": "world", + "foo": [ + "bar" + ] }`, }, { @@ -98,7 +113,22 @@ var stringTestCases = []struct { }`, }, { - name: "multi-line tab indented with leading line breaks", + name: "multi-line tab indented without any leading line-breaks", + s: ` { + "hello": "world", + "foo": [ + "bar" + ] + }`, + want: `{ + "hello": "world", + "foo": [ + "bar" + ] +}`, + }, + { + name: "multi-line tab indented with leading line-breaks", s: ` @@ -197,6 +227,108 @@ world foo bar`, }, + { + name: "long block of text", + s: ` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc + ornare, tellus vel luctus tristique, ipsum ante varius mauris, non + hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis + lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse + rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor + vestibulum pharetra luctus. Vivamus sit amet volutpat elit. + Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut + sem auctor orci sodales varius in ac odio. Nullam finibus odio at + lacus tristique malesuada. + + Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi + ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui + at quam placerat sollicitudin. Proin aliquam justo vitae mauris + gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt + tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse + eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel + sapien eget enim convallis accumsan. Donec dictum ullamcorper leo + placerat sollicitudin. Pellentesque habitant morbi tristique + senectus et netus et malesuada fames ac turpis egestas. Vestibulum + volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut + mauris convallis sodales a et erat. Mauris condimentum augue sit + amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque + mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar + lorem commodo egestas. In facilisis suscipit lacus non suscipit. + + Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus + augue. Quisque ac nibh auctor velit auctor placerat id non eros. + Nulla condimentum quam id risus suscipit, ut fermentum mauris + lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus + ultricies. Sed id ante pretium, accumsan sapien eget, malesuada + quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare + posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, + ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus + efficitur sem. Ut quis placerat quam. Donec maximus a risus sed + posuere. Curabitur pretium a diam non aliquet. + + Class aptent taciti sociosqu ad litora torquent per conubia nostra, + per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci + bibendum ac. Integer eu mattis urna. Donec dictum vehicula + fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, + eget viverra tellus tristique sed. In hac habitasse platea dictumst. + Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc + pretium enim ac urna efficitur, eget facilisis enim interdum. + + Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget + cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis + pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. + Phasellus consequat nibh nec ornare egestas. Donec at pellentesque + lorem.`, + want: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc +ornare, tellus vel luctus tristique, ipsum ante varius mauris, non +hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis +lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse +rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor +vestibulum pharetra luctus. Vivamus sit amet volutpat elit. +Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut +sem auctor orci sodales varius in ac odio. Nullam finibus odio at +lacus tristique malesuada. + +Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi +ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui +at quam placerat sollicitudin. Proin aliquam justo vitae mauris +gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt +tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse +eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel +sapien eget enim convallis accumsan. Donec dictum ullamcorper leo +placerat sollicitudin. Pellentesque habitant morbi tristique +senectus et netus et malesuada fames ac turpis egestas. Vestibulum +volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut +mauris convallis sodales a et erat. Mauris condimentum augue sit +amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque +mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar +lorem commodo egestas. In facilisis suscipit lacus non suscipit. + +Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus +augue. Quisque ac nibh auctor velit auctor placerat id non eros. +Nulla condimentum quam id risus suscipit, ut fermentum mauris +lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus +ultricies. Sed id ante pretium, accumsan sapien eget, malesuada +quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare +posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, +ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus +efficitur sem. Ut quis placerat quam. Donec maximus a risus sed +posuere. Curabitur pretium a diam non aliquet. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, +per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci +bibendum ac. Integer eu mattis urna. Donec dictum vehicula +fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, +eget viverra tellus tristique sed. In hac habitasse platea dictumst. +Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc +pretium enim ac urna efficitur, eget facilisis enim interdum. + +Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget +cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis +pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. +Phasellus consequat nibh nec ornare egestas. Donec at pellentesque +lorem.`, + }, } var stringfTestCases = []struct { @@ -254,6 +386,22 @@ var stringfTestCases = []struct { "foo": [ 42 ] +}`, + }, + { + name: "multi-line space indented without any leading line-breaks", + s: ` { + "hello": "%s", + "foo": [ + %d + ] + }`, + a: []interface{}{"world", 42}, + want: `{ + "hello": "world", + "foo": [ + 42 + ] }`, }, { @@ -292,6 +440,22 @@ var stringfTestCases = []struct { "foo": [ 42 ] +}`, + }, + { + name: "multi-line tab indented without any leading line-breaks", + s: ` { + "hello": "%s", + "foo": [ + %d + ] + }`, + a: []interface{}{"world", 42}, + want: `{ + "hello": "world", + "foo": [ + 42 + ] }`, }, { @@ -400,6 +564,109 @@ world foo 42`, }, + { + name: "long block of text", + s: ` + Lorem %s dolor sit amet, consectetur adipiscing elit. Nunc + ornare, tellus vel luctus tristique, ipsum ante varius mauris, non + hendrerit tellus urna quis ex. %s efficitur arcu sed iaculis + lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse + rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor + vestibulum pharetra luctus. Vivamus sit amet volutpat elit. + Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut + sem auctor orci sodales varius in ac odio. Nullam finibus odio at + lacus tristique malesuada. + + Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi + ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui + at quam placerat sollicitudin. Proin aliquam justo vitae mauris + gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt + tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse + eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel + sapien eget enim convallis accumsan. Donec dictum ullamcorper leo + placerat sollicitudin. Pellentesque habitant morbi tristique + senectus et netus et malesuada fames ac turpis egestas. Vestibulum + volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut + mauris convallis sodales a et erat. Mauris condimentum augue sit + amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque + mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar + lorem commodo egestas. In facilisis suscipit lacus non suscipit. + + Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus + augue. Quisque ac nibh auctor velit auctor placerat id non eros. + Nulla condimentum quam id risus suscipit, ut fermentum mauris + lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus + ultricies. Sed id ante pretium, accumsan sapien eget, malesuada + quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare + posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, + ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus + efficitur sem. Ut quis placerat quam. Donec maximus a risus sed + posuere. Curabitur pretium a diam non aliquet. + + Class aptent taciti sociosqu ad litora torquent per conubia nostra, + per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci + bibendum ac. Integer eu mattis urna. Donec dictum vehicula + fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, + eget viverra tellus tristique sed. In hac habitasse platea dictumst. + Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc + pretium enim ac urna efficitur, eget facilisis enim interdum. + + Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget + cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis + pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. + Phasellus consequat nibh nec ornare egestas. Donec at pellentesque + lorem.`, + a: []interface{}{"ipsum", "Donec"}, + want: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc +ornare, tellus vel luctus tristique, ipsum ante varius mauris, non +hendrerit tellus urna quis ex. Donec efficitur arcu sed iaculis +lobortis. Phasellus facilisis vitae mi quis accumsan. Suspendisse +rhoncus viverra odio ultricies rhoncus. Cras laoreet tortor +vestibulum pharetra luctus. Vivamus sit amet volutpat elit. +Suspendisse feugiat lectus id arcu sollicitudin tincidunt. Duis ut +sem auctor orci sodales varius in ac odio. Nullam finibus odio at +lacus tristique malesuada. + +Morbi nisl nulla, euismod eu enim in, tincidunt varius turpis. Morbi +ullamcorper tortor mi, ut aliquam metus posuere vel. Etiam vel dui +at quam placerat sollicitudin. Proin aliquam justo vitae mauris +gravida porta. Praesent hendrerit egestas ligula, faucibus tincidunt +tortor aliquet at. Ut luctus vehicula arcu eget cursus. Suspendisse +eget enim mollis, condimentum lacus eu, viverra nulla. Aenean vel +sapien eget enim convallis accumsan. Donec dictum ullamcorper leo +placerat sollicitudin. Pellentesque habitant morbi tristique +senectus et netus et malesuada fames ac turpis egestas. Vestibulum +volutpat mattis est, a feugiat purus feugiat at. Quisque at velit ut +mauris convallis sodales a et erat. Mauris condimentum augue sit +amet arcu sodales, aliquet ultrices ipsum tempor. Donec scelerisque +mi ligula, vel volutpat velit posuere a. Sed faucibus dui pulvinar +lorem commodo egestas. In facilisis suscipit lacus non suscipit. + +Nunc dictum est nulla, a rhoncus mi posuere id. Morbi at tempus +augue. Quisque ac nibh auctor velit auctor placerat id non eros. +Nulla condimentum quam id risus suscipit, ut fermentum mauris +lacinia. Vestibulum suscipit rutrum ex, sed vulputate nisi tempus +ultricies. Sed id ante pretium, accumsan sapien eget, malesuada +quam. Phasellus quis commodo enim. Vivamus in purus ac lorem ornare +posuere non eu quam. Sed arcu tortor, gravida quis fringilla nec, +ultricies et sem. Pellentesque arcu enim, tempor id nisl at, rhoncus +efficitur sem. Ut quis placerat quam. Donec maximus a risus sed +posuere. Curabitur pretium a diam non aliquet. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, +per inceptos himenaeos. Morbi volutpat felis leo, vel ultrices orci +bibendum ac. Integer eu mattis urna. Donec dictum vehicula +fermentum. Pellentesque a rutrum ipsum. Donec ultricies elit purus, +eget viverra tellus tristique sed. In hac habitasse platea dictumst. +Duis elementum semper elit, sit amet rhoncus lacus dictum ac. Nunc +pretium enim ac urna efficitur, eget facilisis enim interdum. + +Vivamus vel lectus lacus. Praesent vestibulum vel ligula eget +cursus. Quisque eu dignissim erat. Quisque maximus arcu eu turpis +pulvinar egestas. Nam aliquet neque sed tellus finibus mollis. +Phasellus consequat nibh nec ornare egestas. Donec at pellentesque +lorem.`, + }, } func TestBytes(t *testing.T) {