Up to Main Index                             Up to Journal for April, 2019

                     JOURNAL FOR FRIDAY 26TH APRIL, 2019
______________________________________________________________________________

SUBJECT: Improved + bug fixed text unfolding
   DATE: Fri 26 Apr 20:38:56 BST 2019

Last night I couldn’t sleep and was coding and testing around 3am when I
noticed a problem with the WolfMUD login screen:


  Telnet escape character is '~'.
  Trying 127.0.0.1...
  Connected to 127.0.0.1.
  Escape character is '~'.

  WolfMUD Copyright 1984-2018 Andrew 'Diddymus' Rolfe

      World
      Of
      Living
      Fantasy

  Welcome to WolfMUD!
  Enter your account ID or just press enter to create a new account, enter
  QUIT to leave the server:
  >


Hrm, there should be a blank line just after “Welcome to WolfMUD!”. Now where
did it go? Some debugging later and I’d pulled apart the text.Unfold function.
I then spent about 2 and a half hours ‘fixing’ the issue only to uncover some
more odd corner cases before settling on a final solution. I’ve kept the tests
for the additional corner cases in case they crop up again in the future.

The new Unfold code is simpler and has fewer corner cases to deal with:


  func Unfold(in []byte) []byte {

    // Appending a final line feed to input data and starting with an initial
    // line feed in the output simplifies the code. Both are removed from the
    // final output.
    data := bytes.Runes(append(in, '\n'))
    out := make([]rune, len(data)+1, len(data)+1)
    out[0] = '\n'

    pos := 1
    for x, r := range data {
      if r == '\n' {
        for ; spaceNotLF(out[pos-1]); pos-- { // Trim WS not LF
        }
        if out[pos-1] != '\n' && !spacePrefix(data[x+1:]) {
          r = ' '
        }
      }
      out[pos] = r
      pos++
    }
    return []byte(string(out[1 : pos-1]))
  }


Today I returned to review my changes, sometimes coding through the night
until nearly 6am is not the best idea, and ran some benchmarks.

Of note, text.Unfold now uses two sentinel line feeds to simplify the code by
allowing the removal of some tests. By using “bytes.Runes(append(in, '\n'))”
instead of “append(bytes.Runes(in), '\n')” it results in fewer allocations and
better performance. The results of my benchmarking:


  name                      old time/op   new time/op    delta
  Unfold/The_…own_fox_-4    1.39µs ± 3%   1.15µs ± 6%  -17.54% (p=0.008 n=5+5)
  Unfold/The_…own_fox_#01-4 1.35µs ± 2%   1.25µs ± 2%   -7.41% (p=0.008 n=5+5)
  Unfold/You_…he_corne-4    9.16µs ± 2%   8.13µs ± 4%  -11.18% (p=0.008 n=5+5)

  name                      old alloc/op  new alloc/op   delta
  Unfold/The_…own_fox_-4      816B ± 0%     480B ± 0%  -41.18% (p=0.008 n=5+5)
  Unfold/The_…own_fox_#01-4   816B ± 0%     480B ± 0%  -41.18% (p=0.008 n=5+5)
  Unfold/You_…he_corne-4    6.75kB ± 0%   3.68kB ± 0%  -45.50% (p=0.008 n=5+5)

  name       …             old allocs/op  new allocs/op  delta
  Unfold/The_…own_fox_-4      5.00 ± 0%     4.00 ± 0%  -20.00% (p=0.008 n=5+5)
  Unfold/The_…own_fox_#01-4   5.00 ± 0%     4.00 ± 0%  -20.00% (p=0.008 n=5+5)
  Unfold/You_…he_corne-4      5.00 ± 0%     4.00 ± 0%  -20.00% (p=0.008 n=5+5)


The fix is now available on the public dev branch.

--
Diddymus


  Up to Main Index                             Up to Journal for April, 2019