github.com/gotranspile/cxgo@v0.3.7/examples/potrace/README.md (about)

     1  # Potrace
     2  
     3  This example will guide you trough the process of converting [Potrace](http://potrace.sourceforge.net/) from C to Go.
     4  
     5  ## TL;DR
     6  
     7  ```bash
     8  cd examples/potrace
     9  cxgo
    10  cd ../../.examples/potrace-go
    11  go test ./...
    12  xdg-open stanford.pdf
    13  ```
    14  
    15  If the last command fails, open `stanford.pdf` or `stanford.svg` manually.
    16  
    17  This sequence uses our example [config](cxgo.yml) to fetch Potrace source from the Github,
    18  convert it with `cxgo` as a library (Potrace can also be build as a binary) and traces
    19  an [image](http://potrace.sourceforge.net/img/stanford.pbm) from the official website.
    20  You should see a vectorized output image as a result.
    21  
    22  You just converted the C codebase to Go!
    23  
    24  To learn more about this example, read a guide below.
    25  
    26  ## Prerequisites
    27  
    28  This guide assumes that you have `cxgo` and `git` installed. You don't need anything else.
    29  
    30  ## Getting the C source
    31  
    32  To make things easier, we will pull Potrace code from a [Github mirror](https://github.com/skyrpex/potrace).
    33  This can be done by setting the corresponding config options:
    34  
    35  ```yaml
    36  vcs: https://github.com/skyrpex/potrace.git
    37  branch: '1.15'
    38  ```
    39  
    40  We also need to specify the root for C files, which is `src`:
    41  
    42  ```yaml
    43  root: ./src
    44  ```
    45  
    46  ## Minimal config
    47  
    48  To get started with the translation, we need to set a few basic options.
    49  
    50  First, set the output directory and package name for Go files:
    51  
    52  ```yaml
    53  out: ../../.examples/potrace-go
    54  package: gotrace
    55  ```
    56  
    57  It's always a good idea to force a specific int/pointer type size, so you get the same result regardless of the host.
    58  Let's set int and pointer size to 8 bytes (64 bit):
    59  
    60  ```yaml
    61  int_size: 8
    62  ptr_size: 8
    63  ```
    64  
    65  Now, we need to decide what files to convert. If we look in the source folder, you will see files `potracelib.h` and `potracelib.c`.
    66  Those sounds like a good starting point for a library. To convert both files (cxgo will pick `.h` file automatically),
    67  add the following directives:
    68  
    69  ```yaml
    70  files:
    71    - name: potracelib.c
    72  ```
    73  
    74  We are ready to test it out. You should have the following file:
    75  
    76  ```yaml
    77  vcs: https://github.com/skyrpex/potrace.git
    78  branch: '1.15'
    79  root: ./src
    80  out: ../../.examples/potrace-go
    81  package: gotrace
    82  
    83  int_size: 8
    84  ptr_size: 8
    85  
    86  files:
    87    - name: potracelib.c
    88  ```
    89  
    90  Let's give it a try!
    91  
    92  ```bash
    93  cxgo
    94  ```
    95  
    96  Unfortunately, things are not that simple. You would get an error similar to this:
    97  
    98  ```
    99  potracelib.c: parsing failed: /tmp/potrace/src/potracelib.c:113:24: `VERSION`: expected ;
   100  ```
   101  
   102  From here, an iterative process of fixing the compilation starts.
   103  Don't be too scared though - it might not be as hard as you think.
   104  
   105  ## Make it compile
   106  
   107  In the previous step we were left with the following error:
   108  
   109  ```
   110  potracelib.c: parsing failed: /tmp/potrace/src/potracelib.c:113:24: `VERSION`: expected ;
   111  ```
   112  
   113  If you look for the usages of `VERSION` is becomes clear that it's set to a specific value by the build system.
   114  Since we are just reading the source, that variable is not defined, causing an error.
   115  
   116  We can fix it by adding a custom `#define` directive in the config:
   117  
   118  ```yaml
   119  define:
   120  - name: VERSION
   121    value: '"dev"'
   122  ```
   123  
   124  Running `cxgo` again will succeed this time. Nice! Now we have a first Go file named `potracelib.go`.
   125  
   126  ## Adding more files
   127  
   128  If you check the `potracelib.go` file or try to build it, you will see a lot of Go error complaining about undefined
   129  functions. That's not something scary, we just need to convert more files.
   130  
   131  For example, there are a lot of functions named `progress_*`, so we will add `progress.h`.
   132  
   133  Same for other files:
   134  - `bm_to_pathlist` is defined in `decompose.c`
   135  - `process_path` is defined in `trace.c`
   136  - `path_t` type is defined in `curve.c`
   137  - etc
   138  
   139  If you keep adding new files until there are no undefined identifiers, you may get the following list:
   140  
   141  ```yaml
   142  files:
   143    - name: potracelib.c
   144    - name: progress.h
   145    - name: trace.c
   146    - name: decompose.c
   147    - name: curve.c
   148    - name: bitmap.h
   149    - name: bbox.c
   150    - name: auxiliary.h
   151  ```
   152  
   153  You may have noticed that there are other kinds of errors, so let's examine them.
   154  
   155  ### Duplicate declarations
   156  
   157  Starting from `auxiliary.go`, you may see that `potrace_dpoint_t` and `dpoint_t` are already defined in a few other places.
   158  This is one of the mistakes cxgo might make, and we'll need to remove the duplicates.
   159  
   160  For `potrace_dpoint_t`, it's defined in `auxiliary.go`, `bbox.go` and `potracelib.go` and the definition always looks the same:
   161  
   162  ```go
   163  type potrace_dpoint_t potrace_dpoint_s
   164  ```
   165  
   166  Since this looks like a redefinition of the type, let's just ignore it and ask cxgo to use
   167  the underlying type (`potrace_dpoint_s`) whenever it sees `potrace_dpoint_t`.
   168  
   169  This can be done by adding a `idents` section to the top level of the config:
   170  
   171  ```yaml
   172  idents:
   173    - name: potrace_dpoint_t
   174      alias: true
   175  ```
   176  
   177  Adding to the top level instead of a specific file ensures that it will be used consistently in every file that has this declaration.
   178  
   179  If you check the result by running `cxgo` again, you will notice that we now have `potrace_dpoint_s` defined in all 3 files.
   180  That is the same issue as the last time, but on an underlying type this time:
   181  
   182  ```go
   183  type potrace_dpoint_s struct {
   184  	X float64
   185  	Y float64
   186  }
   187  ```
   188  
   189  The declaration looks good, so let's just remove it from 2 files. Let's say that we want to keep it in `potracelib.go`,
   190  but remove in other files. We can use `skip` to achieve this:
   191  
   192  ```yaml
   193  idents:
   194    - name: potrace_dpoint_t
   195      alias: true
   196  
   197  files:
   198    - name: potracelib.c
   199    - name: progress.h
   200    - name: trace.c
   201    - name: decompose.c
   202    - name: curve.c
   203    - name: bitmap.h
   204    - name: bbox.c
   205      skip:
   206        - potrace_dpoint_s
   207    - name: auxiliary.h
   208      skip:
   209        - potrace_dpoint_s
   210  ```
   211  
   212  We have similar issues with other types (`dpoint_t`, `potrace_path_t`), so let's address them as well:
   213  
   214  ```yaml
   215  idents:
   216    - name: potrace_dpoint_t
   217      alias: true
   218    - name: potrace_path_t
   219      alias: true
   220    - name: dpoint_t
   221      alias: true
   222  
   223  files:
   224    - name: potracelib.c
   225    - name: progress.h
   226    - name: trace.c
   227    - name: decompose.c
   228    - name: curve.c
   229      skip:
   230        - potrace_path_s
   231    - name: bitmap.h
   232    - name: bbox.c
   233      skip:
   234        - potrace_dpoint_s
   235    - name: auxiliary.h
   236      skip:
   237        - potrace_dpoint_s
   238  ```
   239  
   240  This should have solved the duplicate type declarations. But we still have some work to do.
   241  
   242  ### Function name collisions
   243  
   244  Examining error further, you will notice weird issues with functions named `interval`, `iprod` and `bezier`.
   245  
   246  Specifically, if we look at the declarations of `iprod`, we will notice that those are in fact different functions with the same name:
   247  
   248  ```go
   249  // bbox.go
   250  func iprod(a potrace_dpoint_s, b potrace_dpoint_s) float64 {
   251      return a.X*b.X + a.Y*b.Y
   252  }
   253  
   254  // trace.go
   255  func iprod(p0 potrace_dpoint_s, p1 potrace_dpoint_s, p2 potrace_dpoint_s) float64 {
   256      var (
   257          x1 float64
   258          y1 float64
   259          x2 float64
   260          y2 float64
   261      )
   262      x1 = p1.X - p0.X
   263      y1 = p1.Y - p0.Y
   264      x2 = p2.X - p0.X
   265      y2 = p2.Y - p0.Y
   266      return x1*x2 + y1*y2
   267  }
   268  ```
   269  
   270  To fix this name collision, we can use `idents` configs for a specific file (and keep the other name intact):
   271  
   272  ```yaml
   273    - name: trace.c
   274      idents:
   275        - name: iprod
   276          rename: trace_iprod
   277  ```
   278  
   279  This will rename the `iprod` in `trace.go` to `trace_iprod`, while version in `bbox.go` will still be named `iprod`.
   280  
   281  Let's try to do the same for `interval` (in `bbox.go` and `auxiliary.go`):
   282  
   283  ```yaml
   284    - name: auxiliary.h
   285      skip:
   286        - potrace_dpoint_s
   287      idents:
   288        - name: interval
   289          rename: aux_interval
   290  ```
   291  
   292  The declarations are now fixed, but there is another issue now: `trace.go` uses `interval` from `bbox.go`, but expects
   293  a signature from `auxiliary.go` (now named `aux_interval`).
   294  
   295  To fix this we can rename the usage of this function in `trace.go` as well:
   296  
   297  ```yaml
   298    - name: trace.c
   299      idents:
   300        - name: iprod
   301          rename: trace_iprod
   302        - name: interval
   303          rename: aux_interval
   304  ```
   305  
   306  This will now work. Lastly, do the same for `bezier` (defined in `bbox.go` and `trace.go`):
   307  
   308  ```yaml
   309  - name: trace.c
   310    idents:
   311      # ...
   312      - name: bezier
   313        rename: trace_bezier
   314  ```
   315  
   316  We are mostly there! But a few smaller issue remain.
   317  
   318  ### Type conversion issues
   319  
   320  Go and C type systems are slightly different, so you may encounter edge cases not yet recognized by cxgo.
   321  
   322  In those cases, a builtin code replacement function may come in handy.
   323  
   324  In our example, you may find the following function declaration:
   325  
   326  ```go
   327  func bm_free(bm *potrace_bitmap_t) {
   328  	if bm != nil && bm.Map != nil {
   329  		bm_base(bm) = nil
   330  	}
   331  	bm = nil
   332  }
   333  ```
   334  
   335  The problematic line is: `bm_base(bm) = nil`. The original code reads as `free(bm_base(bm))`, but cxgo replaces `free`
   336  with a `nil` pointer assignment, which is invalid when applied to a result of a function call.
   337  
   338  Those issues will eventually be solved in cxgo itself, but for now we can add a workaround:
   339  
   340  ```yaml
   341    - name: bitmap.h
   342      replace:
   343        - old: 'bm_base(bm) = nil'
   344          new: 'bm.Map = nil'
   345  ```
   346  
   347  This will replace all occurrences of `bm_base(bm) = nil` to `bm.Map = nil` in `bitmap.go`.
   348  
   349  Those workarounds are not ideal, but may help to get the project converted without waiting for an upsteam fix.
   350  
   351  ### Undefined types
   352  
   353  Sometimes you may find unused fields or undefined types after the conversion. This is usually due to arcane ways how
   354  private fields can be implemented in C. 
   355  
   356  In our case, you may see that `potrace_privstate_s` is not defined anywhere, and the only usage of the field with this
   357  type is `st.Priv = nil`. We have two options here.
   358  
   359  1. Use the code replacement discussed in the previous section.
   360  
   361  2. Define the missing struct type in a separate Go file.
   362  
   363  The second approach is cleaner, so we will proceed with it. We can either create the file manually, or let cxgo do it:
   364  
   365  ```yaml
   366  files:
   367    # ...
   368    - name: hacks.go
   369      content: |
   370        package gotrace
   371  
   372        type potrace_privstate_s struct{}
   373  ```
   374  
   375  ## Testing attempt
   376  
   377  We now have a first version that should compile. To be on the same page, the config should now contain the following:
   378  
   379  ```yaml
   380  vcs: https://github.com/skyrpex/potrace.git
   381  branch: '1.15'
   382  root: ./src
   383  out: ../../.examples/potrace-go
   384  package: gotrace
   385  int_size: 8
   386  ptr_size: 8
   387  define:
   388    - name: VERSION
   389      value: '"dev"'
   390  
   391  idents:
   392    - name: potrace_dpoint_t
   393      alias: true
   394    - name: potrace_path_t
   395      alias: true
   396    - name: dpoint_t
   397      alias: true
   398  
   399  files:
   400    - name: potracelib.c
   401    - name: progress.h
   402    - name: trace.c
   403      idents:
   404        - name: iprod
   405          rename: trace_iprod
   406        - name: interval
   407          rename: aux_interval
   408        - name: bezier
   409          rename: trace_bezier
   410    - name: decompose.c
   411    - name: curve.c
   412      skip:
   413        - potrace_path_s
   414    - name: bitmap.h
   415      replace:
   416        - old: 'bm_base(bm) = nil'
   417          new: 'bm.Map = nil'
   418    - name: bbox.c
   419      skip:
   420        - potrace_dpoint_s
   421    - name: auxiliary.h
   422      skip:
   423        - potrace_dpoint_s
   424      idents:
   425        - name: interval
   426          rename: aux_interval
   427    - name: hacks.go
   428      content: |
   429        package gotrace
   430  
   431        type potrace_privstate_s struct{}
   432  ```
   433  
   434  We can add a dummy test file to quickly check if we can build the project or not:
   435  
   436  ```yaml
   437  files:
   438    # ...
   439    - name: potrace_test.go
   440      content: |
   441        package gotrace
   442  
   443        import "testing"
   444  
   445        func TestBuild(t *testing.T) {
   446  
   447        }
   448  ```
   449  
   450  Now `go test -v` in the target directory (`../../.examples/potrace-go`) should pass with no errors.
   451  But that's not the real test, of course. Let's write something better:
   452  
   453  ```yaml
   454  files:
   455    # ...
   456    - name: potrace_test.go
   457      content: |
   458        package gotrace
   459  
   460        import (
   461        	"math"
   462        	"testing"
   463  
   464        	"github.com/gotranspile/cxgo/runtime/libc"
   465        )
   466  
   467        func TestPotrace(t *testing.T) {
   468        	bm := bm_new(64, 64)
   469        	if bm == nil {
   470        		t.Fatal(libc.Error())
   471        	}
   472        	*bm.Map = math.MaxUint32
   473  
   474        	p := potrace_param_default()
   475        	st := potrace_trace(p, bm)
   476        	if st == nil {
   477        		t.Fatal(libc.Error())
   478        	} else if st.Status != 0 {
   479        		t.Fatal(st.Status)
   480        	}
   481        	t.Logf("%+v", st)
   482        }
   483  ```
   484  
   485  Here we create a new Potrace bitmap with a size `64x64`, set some values into it, and then trace the bitmap with default
   486  parameters.
   487  
   488  You probably noticed that test checks values for `nil` and prints `libc.Error()`. Since C code uses `errno` to pass errors,
   489  we should call `libc.Error()` to get the error value in case a function fails by returning `nil`.
   490  
   491  Also, the way we set values into a bitmap is weird. The `bm.Map` is a `*potrace_word`, because `cxgo` currently won't
   492  auto-detect slices. Because of this it's hard to set the whole bitmap for testing, so we only set the first word
   493  (where each bit is a pixel value). We will address an issues with a slice a bit later in this guide.
   494  
   495  Now, running this test (`go test -v`) should print something like this: 
   496  
   497  ```
   498  === RUN   TestPotrace
   499      potrace_test.go:24: &{Status:0 Plist:0xc0000d40f0 Priv:<nil>}
   500  --- PASS: TestPotrace (0.00s)
   501  PASS
   502  ok      gotrace 0.002s
   503  ```
   504  
   505  So it looks like we have some traced paths in `st.Plist`. It means that it actually works!
   506  
   507  But the problem is that we can't quickly check the output, since we need to know how to traverse this path list.
   508  So let's convert more files and address issues described above. We can also make the code a bit nicer :)
   509  
   510  ## Handling IO
   511  
   512  Now we need to locate the files related to reading bitmaps from files and writing paths to output files.
   513  
   514  ### Reading bitmaps
   515  
   516  Looks like functions related to reading files are located in `bitmap_io.c`, so let's add it to the config as well.
   517  We would also need `bitops.h` since it contains dependencies for the previous file.
   518  
   519  ```yaml
   520  files:
   521    # ...
   522    - name: bitmap_io.c
   523    - name: bitops.h
   524  ```
   525  
   526  Running the test should succeed, so we will now be able to read bitmaps from files using `bm_read`. This function only
   527  accepts a `stdio.File`, so we need some adapter for it. `cxgo` runtime provides a function for this purpose: `stdio.OpenFrom`.
   528  
   529  Let's extend our test case to read a real bitmap:
   530  
   531  ```go
   532  package gotrace
   533  
   534  import (
   535  	"io"
   536  	"io/ioutil"
   537  	"net/http"
   538  	"os"
   539  	"testing"
   540  
   541  	"github.com/gotranspile/cxgo/runtime/libc"
   542  	"github.com/gotranspile/cxgo/runtime/stdio"
   543  )
   544  
   545  func TestPotrace(t *testing.T) {
   546  	resp, err := http.Get("http://potrace.sourceforge.net/img/stanford.pbm")
   547  	if err != nil {
   548  		t.Fatal(err)
   549  	}
   550  	defer resp.Body.Close()
   551  
   552  	if resp.StatusCode != 200 {
   553  		t.Fatal(resp.Status)
   554  	}
   555  
   556  	tfile, err := ioutil.TempFile("", "potrace_")
   557  	if err != nil {
   558  		t.Fatal(err)
   559  	}
   560  	defer func() {
   561  		_ = tfile.Close()
   562  		_ = os.Remove(tfile.Name())
   563  	}()
   564  
   565  	_, err = io.Copy(tfile, resp.Body)
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	tfile.Seek(0, io.SeekStart)
   570  
   571  	var bm *potrace_bitmap_t
   572  	e := bm_read(stdio.OpenFrom(tfile), 0.5, &bm)
   573  	if e != 0 {
   574  		t.Fatal(e)
   575  	}
   576  
   577  	p := potrace_param_default()
   578  	st := potrace_trace(p, bm)
   579  	if st == nil {
   580  		t.Fatal(libc.Error())
   581  	} else if st.Status != 0 {
   582  		t.Fatal(st.Status)
   583  	}
   584  	t.Logf("%+v", st)
   585  }
   586  ```
   587  
   588  The test is very naive: it downloads an image from the Potrace page, saves it in a temporary file and feeds it into
   589  `potrace_trace`. Of course, we could uses pipes to avoid on-disk files, or cache the file instead downloading it each time,
   590  but for purposes of this guide we will use the simplest option for illustration purposes.
   591  
   592  ### Writing paths
   593  
   594  Now we need to have a way to save paths generated by `potrace_trace`. Potrace has different backends, so let's pick a few
   595  ones that should be the most useful ones: SVG and PDF.
   596  
   597  #### SVG
   598  
   599  First, let's try to add `backend_svg.c` to our config. We'll get a familiar:
   600  
   601  ```
   602  backend_svg.c: parsing failed: /tmp/potrace/src/backend_svg.c:307:31: `POTRACE`: expected , or )
   603  ```
   604  
   605  This is the same issues as we faced with `VERSION`, so let's add another `define`:
   606  
   607  ```yaml
   608  define:
   609    - name: VERSION
   610      value: '"dev"'
   611    - name: POTRACE
   612      value: '"potrace"'
   613  ```
   614  
   615  Now the transpilation succeeds, but we see that an `info` variable is referenced, but is not defined anywhere.
   616  
   617  Looking at the files, it seems like there are two conflicting declarations: one from `main.h` and the second one from `mkbitmap.c`.
   618  It makes sense, because those globals store Potrace configuration for `potrace` and `mkbitmap` binaries.
   619  
   620  This is really unfortunate, since it would be nice to pass this configuration explicitly to avoid confusion for potential
   621  users of our library.
   622  
   623  We can solve it by using `replace`. First, add one of the files (`main.h`), suppress the declaration of a global `info` variable.
   624  Then, use `replace` to rewrite Go code and add one more argument to SVG-related functions with the same name as a global.
   625  This is a bit involved, but should make the API surface much better.
   626  
   627  So, let's add the `main.h` (without `main.c`):
   628  
   629  ```yaml
   630  files:
   631    # ...
   632    - name: main.h
   633      go: backend.go
   634  ```
   635  
   636  Actually, that's all we need here: the header references to `info` as `extern`, and the variable is declared in `main.c`.
   637  We are almost done here, but we now need to fix a few more undefined types:
   638  
   639  ```yaml
   640  files:
   641    # ...
   642    - name: trans.c
   643    - name: progress_bar.c
   644  ```
   645  
   646  Next, let's start adding replacement directives, starting from the top-level API functions:
   647  
   648  ```yaml
   649  files:
   650    # ...
   651    - name: backend_svg.c
   652      replace:
   653        - old: 'func page_svg('
   654          new: 'func page_svg(info *info_s, '
   655        - old: 'func page_gimp('
   656          new: 'func page_gimp(info *info_s, '
   657  ```
   658  
   659  And fix the function calls to them as well:
   660  
   661  ```yaml
   662      replace:
   663        # ...
   664        - old: 'page_svg(fout'
   665          new: 'page_svg(info, fout'
   666  ```
   667  
   668  Now, let's repeat this for all other functions in this file that depend on `info`. The result should look like this:
   669  
   670  ```yaml
   671    - name: backend_svg.c
   672      replace:
   673        - old: 'func unit('
   674          new: 'func unit(info *info_s, '
   675        - old: 'func svg_moveto('
   676          new: 'func svg_moveto(info *info_s, '
   677        - old: 'func svg_rmoveto('
   678          new: 'func svg_rmoveto(info *info_s, '
   679        - old: 'func svg_lineto('
   680          new: 'func svg_lineto(info *info_s, '
   681        - old: 'func svg_curveto('
   682          new: 'func svg_curveto(info *info_s, '
   683        - old: 'func svg_path('
   684          new: 'func svg_path(info *info_s, '
   685        - old: 'func svg_jaggy_path('
   686          new: 'func svg_jaggy_path(info *info_s, '
   687        - old: 'func write_paths_opaque('
   688          new: 'func write_paths_opaque(info *info_s, '
   689        - old: 'func write_paths_transparent_rec('
   690          new: 'func write_paths_transparent_rec(info *info_s, '
   691        - old: 'func write_paths_transparent('
   692          new: 'func write_paths_transparent(info *info_s, '
   693        - old: 'func page_svg('
   694          new: 'func page_svg(info *info_s, '
   695        - old: 'func page_gimp('
   696          new: 'func page_gimp(info *info_s, '
   697        - old: 'unit(p)'
   698          new: 'unit(info, p)'
   699        - old: 'unit(p1)'
   700          new: 'unit(info, p1)'
   701        - old: 'unit(p2)'
   702          new: 'unit(info, p2)'
   703        - old: 'unit(p3)'
   704          new: 'unit(info, p3)'
   705        - old: 'svg_moveto(fout'
   706          new: 'svg_moveto(info, fout'
   707        - old: 'svg_rmoveto(fout'
   708          new: 'svg_rmoveto(info, fout'
   709        - old: 'svg_lineto(fout'
   710          new: 'svg_lineto(info, fout'
   711        - old: 'svg_curveto(fout'
   712          new: 'svg_curveto(info, fout'
   713        - old: 'svg_jaggy_path(fout'
   714          new: 'svg_jaggy_path(info, fout'
   715        - old: 'svg_path(fout'
   716          new: 'svg_path(info, fout'
   717        - old: 'write_paths_opaque(fout'
   718          new: 'write_paths_opaque(info, fout'
   719        - old: 'write_paths_transparent_rec(fout'
   720          new: 'write_paths_transparent_rec(info, fout'
   721        - old: 'write_paths_transparent(fout'
   722          new: 'write_paths_transparent(info, fout'
   723        - old: 'page_svg(fout'
   724          new: 'page_svg(info, fout'
   725  ```
   726  
   727  Transpiling it will only show that `backend_s` is undefined. Since it's unused in the code, let's add a stub for it:
   728  
   729  ```yaml
   730    - name: hacks.go
   731      content: |
   732        package gotrace
   733  
   734        type backend_s struct{}
   735        type potrace_privstate_s struct{}
   736  ```
   737  
   738  It should now compile again, and we can use new functions in our test:
   739  
   740  ```go
   741  package gotrace
   742  
   743  import (
   744  	"io"
   745  	"io/ioutil"
   746  	"net/http"
   747  	"os"
   748  	"testing"
   749  
   750  	"github.com/gotranspile/cxgo/runtime/libc"
   751  	"github.com/gotranspile/cxgo/runtime/stdio"
   752  )
   753  
   754  func TestPotrace(t *testing.T) {
   755  	resp, err := http.Get("http://potrace.sourceforge.net/img/stanford.pbm")
   756  	if err != nil {
   757  		t.Fatal(err)
   758  	}
   759  	defer resp.Body.Close()
   760  
   761  	if resp.StatusCode != 200 {
   762  		t.Fatal(resp.Status)
   763  	}
   764  
   765  	tfile, err := ioutil.TempFile("", "potrace_")
   766  	if err != nil {
   767  		t.Fatal(err)
   768  	}
   769  	defer func() {
   770  		_ = tfile.Close()
   771  		_ = os.Remove(tfile.Name())
   772  	}()
   773  
   774  	_, err = io.Copy(tfile, resp.Body)
   775  	if err != nil {
   776  		t.Fatal(err)
   777  	}
   778  	tfile.Seek(0, io.SeekStart)
   779  
   780  	var bm *potrace_bitmap_t
   781  	e := bm_read(stdio.OpenFrom(tfile), 0.5, &bm)
   782  	if e != 0 {
   783  		t.Fatal(e)
   784  	}
   785  
   786  	p := potrace_param_default()
   787  	st := potrace_trace(p, bm)
   788  	if st == nil {
   789  		t.Fatal(libc.Error())
   790  	} else if st.Status != 0 {
   791  		t.Fatal(st.Status)
   792  	}
   793  	var tr trans_t
   794  	trans_from_rect(&tr, float64(bm.W), float64(bm.H))
   795  	bi := &info_s{
   796  		Unit: 1,
   797  	}
   798  	iinfo := &imginfo_t{
   799  		Pixwidth: bm.W, Pixheight: bm.H,
   800  		Width: float64(bm.W), Height: float64(bm.H),
   801  		Trans: tr,
   802  	}
   803  
   804  	svgOut, err := os.Create("stanford.svg")
   805  	if err != nil {
   806  		t.Fatal(err)
   807  	}
   808  	defer svgOut.Close()
   809  	page_svg(bi, stdio.OpenFrom(svgOut), st.Plist, iinfo)
   810  }
   811  ```
   812  
   813  Test should again succeed and generate a `stanford.svg` files that can be viewed in the browser.
   814  
   815  #### PDF
   816  
   817  We promised to have PDF support as well, so let's try to add `backend_pdf.c` as well.
   818  
   819  Of course, we will see similar issues with `info` here as well. 
   820  
   821  But, before addressing these, we can also see that some
   822  functions have conflicts in those two backends files. We already know how to solve it:
   823  
   824  ```yaml
   825  files:
   826    # ...
   827    - name: backend_pdf.c
   828      idents:
   829        - name: unit
   830          rename: pdf_unit
   831        - name: ship
   832          rename: pdf_ship
   833  ```
   834  
   835  Now, let's again add replacements for `info`:
   836  
   837  ```yaml
   838    - name: backend_pdf.c
   839      # ...
   840      replace:
   841        - old: 'func render0('
   842          new: 'func render0(info *info_s, '
   843        - old: 'func render0_opaque('
   844          new: 'func render0_opaque(info *info_s, '
   845        - old: 'func pdf_render('
   846          new: 'func pdf_render(info *info_s, '
   847        - old: 'func pdf_callbacks('
   848          new: 'func pdf_callbacks(info *info_s, '
   849        - old: 'func pdf_unit('
   850          new: 'func pdf_unit(info *info_s, '
   851        - old: 'func pdf_coords('
   852          new: 'func pdf_coords(info *info_s, '
   853        - old: 'func pdf_moveto('
   854          new: 'func pdf_moveto(info *info_s, '
   855        - old: 'func pdf_lineto('
   856          new: 'func pdf_lineto(info *info_s, '
   857        - old: 'func pdf_curveto('
   858          new: 'func pdf_curveto(info *info_s, '
   859        - old: 'func pdf_path('
   860          new: 'func pdf_path(info *info_s, '
   861        - old: 'func pdf_pageinit('
   862          new: 'func pdf_pageinit(info *info_s, '
   863        - old: 'func page_pdfpage('
   864          new: 'func page_pdfpage(info *info_s, '
   865        - old: 'func page_pdf('
   866          new: 'func page_pdf(info *info_s, '
   867        - old: 'func init_pdf('
   868          new: 'func init_pdf(info *info_s, '
   869        - old: 'func term_pdf('
   870          new: 'func term_pdf(info *info_s, '
   871        - old: 'render0_opaque(plist)'
   872          new: 'render0_opaque(info, plist)'
   873        - old: 'render0(plist)'
   874          new: 'render0(info, plist)'
   875        - old: 'pdf_unit(p'
   876          new: 'pdf_unit(info, p'
   877        - old: 'pdf_coords(p'
   878          new: 'pdf_coords(info, p'
   879        - old: 'pdf_moveto(*'
   880          new: 'pdf_moveto(info, *'
   881        - old: 'pdf_lineto(*'
   882          new: 'pdf_lineto(info, *'
   883        - old: 'pdf_curveto(*'
   884          new: 'pdf_curveto(info, *'
   885        - old: 'pdf_callbacks(fout'
   886          new: 'pdf_callbacks(info, fout'
   887        - old: 'pdf_pageinit(imginfo'
   888          new: 'pdf_pageinit(info, imginfo'
   889        - old: 'pdf_render(plist'
   890          new: 'pdf_render(info, plist'
   891        - old: 'pdf_path(&'
   892          new: 'pdf_path(info, &'
   893  ```
   894  
   895  We notice that `dummy_xship` and `pdf_xship`. They are defined in `flate.c`, so we add it too.
   896  
   897  Since we don't really need anything from that file except those two functions so let's skip `lzw_xship` that causes more errors.
   898  
   899  ```yaml
   900  files:
   901    # ...
   902    - name: flate.c
   903      skip:
   904        - lzw_xship
   905  ```
   906  
   907  And finally, let's add a few more lines to our test to generate a PDF as well:
   908  
   909  
   910  ```go
   911  pdfOut, err := os.Create("stanford.pdf")
   912  if err != nil {
   913      t.Fatal(err)
   914  }
   915  defer pdfOut.Close()
   916  init_pdf(bi, stdio.OpenFrom(pdfOut))
   917  page_pdf(bi, stdio.OpenFrom(pdfOut), st.Plist, iinfo)
   918  term_pdf(bi, stdio.OpenFrom(pdfOut))
   919  ```
   920  
   921  Test should succeed and generate a `stanford.pdf` files that should be more convenient to check.
   922  
   923  ## Improving the code
   924  
   925  At this stage we have a working prototype, but the code might not be as nice as it could be.
   926  
   927  Here are some issues we have:
   928  
   929  - Fixed int types (`int64`) instead of Go `int`
   930  - C names for types and functions
   931  - Int types where we want bool types
   932  - Pointer types where we want to have slices
   933  
   934  We will now show how to address these issues.
   935  
   936  To be on the same page, your config should look like this:
   937  
   938  ```yaml
   939  vcs: https://github.com/skyrpex/potrace.git
   940  branch: '1.15'
   941  root: ./src
   942  out: ../../.examples/potrace-go
   943  package: gotrace
   944  int_size: 8
   945  ptr_size: 8
   946  define:
   947    - name: VERSION
   948      value: '"dev"'
   949    - name: POTRACE
   950      value: '"potrace"'
   951  
   952  idents:
   953    - name: potrace_dpoint_t
   954      alias: true
   955    - name: potrace_path_t
   956      alias: true
   957    - name: dpoint_t
   958      alias: true
   959  
   960  files:
   961    - name: potracelib.c
   962    - name: progress.h
   963    - name: trace.c
   964      idents:
   965        - name: iprod
   966          rename: trace_iprod
   967        - name: interval
   968          rename: aux_interval
   969        - name: bezier
   970          rename: trace_bezier
   971    - name: decompose.c
   972    - name: curve.c
   973      skip:
   974        - potrace_path_s
   975    - name: bitmap.h
   976      replace:
   977        - old: 'bm_base(bm) = nil'
   978          new: 'bm.Map = nil'
   979    - name: bbox.c
   980      skip:
   981        - potrace_dpoint_s
   982    - name: auxiliary.h
   983      skip:
   984        - potrace_dpoint_s
   985      idents:
   986        - name: interval
   987          rename: aux_interval
   988    - name: bitmap_io.c
   989    - name: bitops.h
   990    - name: backend_svg.c
   991      replace:
   992        - old: 'func unit('
   993          new: 'func unit(info *info_s, '
   994        - old: 'func svg_moveto('
   995          new: 'func svg_moveto(info *info_s, '
   996        - old: 'func svg_rmoveto('
   997          new: 'func svg_rmoveto(info *info_s, '
   998        - old: 'func svg_lineto('
   999          new: 'func svg_lineto(info *info_s, '
  1000        - old: 'func svg_curveto('
  1001          new: 'func svg_curveto(info *info_s, '
  1002        - old: 'func svg_path('
  1003          new: 'func svg_path(info *info_s, '
  1004        - old: 'func svg_jaggy_path('
  1005          new: 'func svg_jaggy_path(info *info_s, '
  1006        - old: 'func write_paths_opaque('
  1007          new: 'func write_paths_opaque(info *info_s, '
  1008        - old: 'func write_paths_transparent_rec('
  1009          new: 'func write_paths_transparent_rec(info *info_s, '
  1010        - old: 'func write_paths_transparent('
  1011          new: 'func write_paths_transparent(info *info_s, '
  1012        - old: 'func page_svg('
  1013          new: 'func page_svg(info *info_s, '
  1014        - old: 'func page_gimp('
  1015          new: 'func page_gimp(info *info_s, '
  1016        - old: 'unit(p)'
  1017          new: 'unit(info, p)'
  1018        - old: 'unit(p1)'
  1019          new: 'unit(info, p1)'
  1020        - old: 'unit(p2)'
  1021          new: 'unit(info, p2)'
  1022        - old: 'unit(p3)'
  1023          new: 'unit(info, p3)'
  1024        - old: 'svg_moveto(fout'
  1025          new: 'svg_moveto(info, fout'
  1026        - old: 'svg_rmoveto(fout'
  1027          new: 'svg_rmoveto(info, fout'
  1028        - old: 'svg_lineto(fout'
  1029          new: 'svg_lineto(info, fout'
  1030        - old: 'svg_curveto(fout'
  1031          new: 'svg_curveto(info, fout'
  1032        - old: 'svg_jaggy_path(fout'
  1033          new: 'svg_jaggy_path(info, fout'
  1034        - old: 'svg_path(fout'
  1035          new: 'svg_path(info, fout'
  1036        - old: 'write_paths_opaque(fout'
  1037          new: 'write_paths_opaque(info, fout'
  1038        - old: 'write_paths_transparent_rec(fout'
  1039          new: 'write_paths_transparent_rec(info, fout'
  1040        - old: 'write_paths_transparent(fout'
  1041          new: 'write_paths_transparent(info, fout'
  1042        - old: 'page_svg(fout'
  1043          new: 'page_svg(info, fout'
  1044    - name: flate.c
  1045      skip:
  1046        - lzw_xship
  1047    - name: backend_pdf.c
  1048      idents:
  1049        - name: unit
  1050          rename: pdf_unit
  1051        - name: ship
  1052          rename: pdf_ship
  1053      replace:
  1054        - old: 'func render0('
  1055          new: 'func render0(info *info_s, '
  1056        - old: 'func render0_opaque('
  1057          new: 'func render0_opaque(info *info_s, '
  1058        - old: 'func pdf_render('
  1059          new: 'func pdf_render(info *info_s, '
  1060        - old: 'func pdf_callbacks('
  1061          new: 'func pdf_callbacks(info *info_s, '
  1062        - old: 'func pdf_unit('
  1063          new: 'func pdf_unit(info *info_s, '
  1064        - old: 'func pdf_coords('
  1065          new: 'func pdf_coords(info *info_s, '
  1066        - old: 'func pdf_moveto('
  1067          new: 'func pdf_moveto(info *info_s, '
  1068        - old: 'func pdf_lineto('
  1069          new: 'func pdf_lineto(info *info_s, '
  1070        - old: 'func pdf_curveto('
  1071          new: 'func pdf_curveto(info *info_s, '
  1072        - old: 'func pdf_path('
  1073          new: 'func pdf_path(info *info_s, '
  1074        - old: 'func pdf_pageinit('
  1075          new: 'func pdf_pageinit(info *info_s, '
  1076        - old: 'func page_pdfpage('
  1077          new: 'func page_pdfpage(info *info_s, '
  1078        - old: 'func page_pdf('
  1079          new: 'func page_pdf(info *info_s, '
  1080        - old: 'func init_pdf('
  1081          new: 'func init_pdf(info *info_s, '
  1082        - old: 'func term_pdf('
  1083          new: 'func term_pdf(info *info_s, '
  1084        - old: 'render0_opaque(plist)'
  1085          new: 'render0_opaque(info, plist)'
  1086        - old: 'render0(plist)'
  1087          new: 'render0(info, plist)'
  1088        - old: 'pdf_unit(p'
  1089          new: 'pdf_unit(info, p'
  1090        - old: 'pdf_coords(p'
  1091          new: 'pdf_coords(info, p'
  1092        - old: 'pdf_moveto(*'
  1093          new: 'pdf_moveto(info, *'
  1094        - old: 'pdf_lineto(*'
  1095          new: 'pdf_lineto(info, *'
  1096        - old: 'pdf_curveto(*'
  1097          new: 'pdf_curveto(info, *'
  1098        - old: 'pdf_callbacks(fout'
  1099          new: 'pdf_callbacks(info, fout'
  1100        - old: 'pdf_pageinit(imginfo'
  1101          new: 'pdf_pageinit(info, imginfo'
  1102        - old: 'pdf_render(plist'
  1103          new: 'pdf_render(info, plist'
  1104        - old: 'pdf_path(&'
  1105          new: 'pdf_path(info, &'
  1106    - name: main.h
  1107      go: backend.go
  1108    - name: trans.c
  1109    - name: progress_bar.c
  1110    - name: hacks.go
  1111      content: |
  1112        package gotrace
  1113  
  1114        type backend_s struct{}
  1115        type potrace_privstate_s struct{}
  1116    - name: potrace_test.go
  1117      content: |
  1118        package gotrace
  1119  
  1120        import (
  1121        	"io"
  1122        	"io/ioutil"
  1123        	"net/http"
  1124        	"os"
  1125        	"testing"
  1126  
  1127        	"github.com/gotranspile/cxgo/runtime/libc"
  1128        	"github.com/gotranspile/cxgo/runtime/stdio"
  1129        )
  1130  
  1131        func TestPotrace(t *testing.T) {
  1132        	resp, err := http.Get("http://potrace.sourceforge.net/img/stanford.pbm")
  1133        	if err != nil {
  1134        		t.Fatal(err)
  1135        	}
  1136        	defer resp.Body.Close()
  1137  
  1138        	if resp.StatusCode != 200 {
  1139        		t.Fatal(resp.Status)
  1140        	}
  1141  
  1142        	tfile, err := ioutil.TempFile("", "potrace_")
  1143        	if err != nil {
  1144        		t.Fatal(err)
  1145        	}
  1146        	defer func() {
  1147        		_ = tfile.Close()
  1148        		_ = os.Remove(tfile.Name())
  1149        	}()
  1150  
  1151        	_, err = io.Copy(tfile, resp.Body)
  1152        	if err != nil {
  1153        		t.Fatal(err)
  1154        	}
  1155        	tfile.Seek(0, io.SeekStart)
  1156  
  1157        	var bm *potrace_bitmap_t
  1158        	e := bm_read(stdio.OpenFrom(tfile), 0.5, &bm)
  1159        	if e != 0 {
  1160        		t.Fatal(e)
  1161        	}
  1162  
  1163        	p := potrace_param_default()
  1164        	st := potrace_trace(p, bm)
  1165        	if st == nil {
  1166        		t.Fatal(libc.Error())
  1167        	} else if st.Status != 0 {
  1168        		t.Fatal(st.Status)
  1169        	}
  1170        	var tr trans_t
  1171        	trans_from_rect(&tr, float64(bm.W), float64(bm.H))
  1172        	bi := &info_s{
  1173        		Unit: 1,
  1174        	}
  1175        	iinfo := &imginfo_t{
  1176        		Pixwidth: bm.W, Pixheight: bm.H,
  1177        		Width: float64(bm.W), Height: float64(bm.H),
  1178        		Trans: tr,
  1179        	}
  1180  
  1181        	svgOut, err := os.Create("stanford.svg")
  1182        	if err != nil {
  1183        		t.Fatal(err)
  1184        	}
  1185        	defer svgOut.Close()
  1186        	page_svg(bi, stdio.OpenFrom(svgOut), st.Plist, iinfo)
  1187  
  1188        	pdfOut, err := os.Create("stanford.pdf")
  1189        	if err != nil {
  1190        		t.Fatal(err)
  1191        	}
  1192        	defer pdfOut.Close()
  1193        	init_pdf(bi, stdio.OpenFrom(pdfOut))
  1194        	page_pdf(bi, stdio.OpenFrom(pdfOut), st.Plist, iinfo)
  1195        	term_pdf(bi, stdio.OpenFrom(pdfOut))
  1196        }
  1197  ```
  1198  
  1199  ### Using Go ints
  1200  
  1201  Since we can now ensure that the code works correctly, we can experiment with the types. `cxgo` allows using Go `int`
  1202  instead of fixed-size ints generated by default. Add the following line to the config:
  1203  
  1204  ```yaml
  1205  use_go_int: true
  1206  ```
  1207  
  1208  Test should lead to exactly the same result. For some projects it might be false, so use this option with a caution.
  1209  
  1210  ### Better names
  1211  
  1212  `cxgo` won't rename functions and types by default, but we can instruct it to do so, if desired.
  1213  
  1214  For example, let's rename the functions use in tests and types in `potracelib.go`. We will also remove duplicates
  1215  like `type_t` and `type_s` to make the code cleaner.
  1216  
  1217  ```yaml
  1218  idents:
  1219    # rename identifiers
  1220    - name: potrace_progress_s
  1221      rename: Progress
  1222    - name: potrace_param_s
  1223      rename: Param
  1224    - name: potrace_word
  1225      rename: Word
  1226    - name: potrace_bitmap_s
  1227      rename: Bitmap
  1228    - name: potrace_dpoint_s
  1229      rename: DPoint
  1230    - name: potrace_curve_s
  1231      rename: Curve
  1232    - name: potrace_path_s
  1233      rename: Path
  1234    - name: potrace_state_s
  1235      rename: State
  1236    - name: trans_s
  1237      rename: Trans
  1238    - name: imginfo_s
  1239      rename: ImgInfo
  1240    - name: info_s
  1241      rename: BackendInfo
  1242    - name: dim_s
  1243      rename: Dim
  1244    - name: potrace_param_default
  1245      rename: ParamDefault
  1246    - name: potrace_trace
  1247      rename: Trace
  1248    # cleanup duplicate types
  1249    - name: potrace_dpoint_t
  1250      alias: true
  1251    - name: potrace_path_t
  1252      alias: true
  1253    - name: dpoint_t
  1254      alias: true
  1255    - name: potrace_progress_t
  1256      alias: true
  1257    - name: potrace_param_t
  1258      alias: true
  1259    - name: potrace_bitmap_t
  1260      alias: true
  1261    - name: potrace_curve_t
  1262      alias: true
  1263    - name: potrace_state_t
  1264      alias: true
  1265    - name: trans_t
  1266      alias: true
  1267    - name: imginfo_t
  1268      alias: true
  1269    - name: info_t
  1270      alias: true
  1271    - name: dim_t
  1272      alias: true
  1273  ```
  1274  
  1275  Also, don't forget to adjust test file and the `replace` directives. We can continue improving names in other files,
  1276  but there are other issues to address as well.
  1277  
  1278  ### Using Go bools
  1279  
  1280  C code doesn't always use `bool` where it should. `cxgo` has a way to give a hint about those fields/arguments.
  1281  
  1282  For example, in our `BackendInfo` struct type (former `info_s`) we have a name called `Debug`. If we check usages,
  1283  it's clear that it's used as a boolean value.
  1284  
  1285  We can add the following to the `BackendInfo` definition to force `bool` type here:
  1286  
  1287  ```yaml
  1288  idents:
  1289    # ...
  1290    - name: info_s
  1291      rename: BackendInfo
  1292      fields:
  1293        - name: debug
  1294          type: bool
  1295  ```
  1296  
  1297  Note that `debug` is a C name, not a name that you see in Go (`Debug`).
  1298  
  1299  We can do the same for other fields like `Compress`, `Opaque`, etc.
  1300  
  1301  ### Using slices
  1302  
  1303  C doesn't have a notion of slices and unfortunately `cxgo` is not smart enough to detect those automatically.
  1304  But again, we have a way to give it a hint for a specific struct field or function argument.
  1305  
  1306  We can start with promoting `Bitmap.Map` (`potrace_bitmap_t.map`) to a slice:
  1307  
  1308  ```yaml
  1309  idents:
  1310    # ...
  1311    - name: potrace_bitmap_s
  1312      rename: Bitmap
  1313      fields:
  1314        - name: map
  1315          type: slice
  1316  ```
  1317  
  1318  Unfortunately this won't compile: there are some unusual slice usages in the code.
  1319  
  1320  First, this one:
  1321  
  1322  ```go
  1323  bm.Map = ([]Word)((*Word)(libc.Calloc(1, int(size))))
  1324  ```
  1325  
  1326  In fact, `cxgo` supports converting `calloc` to `make`, but it gots confused by `size` variable instead of `sizeof(T)`.
  1327  We can help it with `replace` (on `bitmap.h`):
  1328  
  1329  ```yaml
  1330  files:
  1331    # ...
  1332    - name: bitmap.h
  1333      replace:
  1334        - old: 'bm_base(bm) = nil'
  1335          new: 'bm.Map = nil'
  1336        - old: 'bm.Map = ([]Word)((*Word)(libc.Calloc(1, int(size))))'
  1337          new: 'bm.Map = make([]Word, uintptr(size)/unsafe.Sizeof(Word(0)))'
  1338  ```
  1339  
  1340  The second usage is the following (in `bm_flip`):
  1341  
  1342  ```yaml
  1343  bm.Map = ([]Word)(&bm.Map[int64(bm.H-1)*int64(bm.Dy)])
  1344  bm.Dy = -dy
  1345  ```
  1346  
  1347  Potrace author doe a trick to flip the bitmap: he sets a pointer to the last element instead of the first and makes the
  1348  stride (row offset multiplier) negative. This way the indexing will automatically go backward.
  1349  
  1350  Although it's a nice trick, Go won't allow this trickery on slices. So we have to disable this function and reimplement
  1351  it later in a different way (e.g. checking `Dy` sign and change how we index the slice).
  1352  
  1353  Disabling can be done with `skip` on `bitmap.h`, and we will add a stub for into `hacks.go` since it's actually use in the code:
  1354  
  1355  ```yaml
  1356  files:
  1357    # ...
  1358    - name: bitmap.h
  1359      skip:
  1360        - bm_flip
  1361    # ...
  1362    - name: hacks.go
  1363      content: |
  1364        package gotrace
  1365  
  1366        // C flips bitmaps by using negative bitmap strides, which we cannot represent in Go with slices
  1367  
  1368        func bm_flip(bm *Bitmap) {
  1369            // TODO: implement
  1370        }
  1371  
  1372        type backend_s struct{}
  1373        type potrace_privstate_s struct{}
  1374  ```
  1375  
  1376  And finally, the last issue is:
  1377  
  1378  ```go
  1379  newmap = (*Word)(libc.Realloc(unsafe.Pointer(&bm.Map[0]), int(newsize)))
  1380  if newmap == nil {
  1381      goto error
  1382  }
  1383  bm.Map = ([]Word)(newmap)
  1384  ```
  1385  
  1386  `cxgo` doesn't translate `realloc` yet for slices, and even if it did, the local variable `newmap` still has a pointer type.
  1387  So we need to make at least two replacements here (plus one cosmetic):
  1388  
  1389  ```yaml
  1390      replace:
  1391        # ...
  1392        - old: 'newmap = (*Word)(libc.Realloc(unsafe.Pointer(&bm.Map[0]), int(newsize)))'
  1393          new: 'newmap = make([]Word, uintptr(newsize)/unsafe.Sizeof(Word(0))); copy(newmap, bm.Map)'
  1394        - old: 'newmap  *Word'
  1395          new: 'newmap []Word'
  1396        - old: 'bm.Map = ([]Word)(newmap)'
  1397          new: 'bm.Map = newmap'
  1398  ```
  1399  
  1400  This time it should compile, and the tests should still pass.