Kothar Labs Contact Me

CSView 2.0 Alpha 1
Mon, 25 May 2020 / projects / csview / macos

CSView 2.0 Alpha 1 for MacOS is now available for download!

Download CSView 2.0 Alpha 1 - macOS 10.12+, 64-bit, 3.5MB

Version 2.0 is a macOS native application, bringing performance and visual improvements.

  • Startup time is improved over the 1.x implementation
  • UI rendering is now able to take advantage of native graphics acceleration
  • Dark/Light theme is picked up from your OS settings
  • macOS UI features such as tabbed document windows come for free
  • Larger number of rows supported (rendering tested with 10B+ rows and columns)
  • More opportunity for future performance improvements and new features

Alpha 1 doesn't yet support all the features of the 1.x releases, such as search and custom column delimiters. These will be coming in future alphas before the official 2.0 release.

I have a prototype grid renderer in the works for Windows too, but I'm focusing on getting a fully working Mac release ready first.

Comments |

Screenshot of CSView 2.0 Alpha 1

MacOS Catalina — Notarization and App Store Signing with Java 11
Sun, 13 Oct 2019 / projects / blog / csview

With the release of macOS Catalina (10.15) there have been some changes to the way software needs to be signed. In particular, Apple has introduced a new ‘Notarization’ service which allows developers to pre-register the digital signature of an application so that it can be later checked by the operating system when a user downloads software outside the App Store.

Earlier versions of macOS still required software to be signed by the developer to allow it to run on a user’s system, however notarization takes this one step further, and presumably allows Apple to disable malicious applications when macOS phones home to check if the app has been notarized.

If you’re using Xcode for development and packaging of your app, this is all handled for you in the latest versions. Unfortunately, CSView is written in Java, requiring a custom build and packaging process, and this process has not survived the update to Catalina. It appears that some of the existing configuration was not compatible with the new sandboxing requirements in Catalina, so simply notarizing the old versions of the app was not sufficient, even for versions distributed through the App Store.

I took this as a good opportunity to update to Java 11, and the rest of this article covers the changes I’ve needed to make to the packaging process.

Comments |

Ditto Open Beta
Sun, 23 Jun 2019 / projects / asana / ditto

Ditto is a tool for transferring Asana projects between workspaces. It is a complete rewrite of an earlier project with the unwieldy title 'Organise Asana Projects' but colloquially known as 'Kothar' by the Asana community.

Ditto, or 'Kothar 2', fixes a number of longstanding issues with the original implementation, allowing faster and more accurate project copies to be created. It's also much more pleasant to work with from the development side, which means I am more inclined to spend time updating and improving it.

Comments |

Ditto Logo - Beta

Streaming multipart HTTP requests in Go
Sat, 23 Feb 2019 / blog / go

Having seen Peter Bourgon's post on multipart http responses come up in the Golang Weekly newsletter, I thought I would write some notes on a related problem I was looking at recently: how to stream a multipart upload to a remote HTTP server.

My specific use case is to receive a stream from AWS S3, and pipe it into the upload API for Asana. It's probably ending up back in S3, but that's neither here nor there.

The multipart writer provided with Go's standard library makes serving multipart responses really easy, but sending an HTTP request needs an io.Reader to provide a streaming request body, which doesn't fit nicely with the io.Writer based support.

I found a couple of answers solving this problem, either by manually encoding the multipart headers or by running a separate goroutine to write into a pipe which can then be read by the HTTP client.

I settled on something in between: writing the headers to a byte buffer, then slicing the buffer and composing an io.MultiReader from the parts and the body reader from the S3 getObject response.

// Error checking omitted
output, _ := r.s3.GetObject(&s3.GetObjectInput{
    Bucket: aws.String(bucket),
    Key:    aws.String(key),

var contentType string
if output.ContentType != nil {
    contentType = *output.ContentType
} else {
    contentType = "application/octet-stream"

// Write header
buffer := &bytes.Buffer{}
partWriter := multipart.NewWriter(buffer)
h := make(textproto.MIMEHeader)
    fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
        escapeQuotes(field), escapeQuotes(filename)))
h.Set("Content-Type", contentType)

headerSize := buffer.Len()

// Write footer

// Create request
request, _ := http.NewRequest(http.MethodPost, c.getURL(path), io.MultiReader(

request.Header.Add("Content-Type", partWriter.FormDataContentType())
resp, _ := httpClient.Do(request)

This worked well, and avoids needing to read the whole object into memory or caching it to disk. A cleaner solution might be to implement a reader-based equivalent in the multipart package.


Sat, 24 Feb 2018 / projects / java / compactlist

A memory-efficient List<Long> for Java

CompactList implements the List<Long> interface, but internally it uses a tree of variable word-width segments to improve performance and memory usage compared to an ArrayList.

Performance tends to be worse for appends than an ArrayList but better for inserts. Memory usage is significantly reduced, even for incompressible random data where it approaches the memory use of an array of primitive longs (which happens to be the internal representation in this case).

There are some performance metrics over at the GitHub repository. My aim is to use this for the internal index representation in CSView, which already uses an earlier version of this data structure.

Comments |

Memory usage during random insert for CompactList and other List<Long> implementations

Generic Adapters in Go
Sun, 13 Aug 2017 / blog / go / generics

While playing about with ideas for generic types in Go, I have come up with what I'm calling a 'Generic Adapter'.

The approach I've taken here is essentialy that described by Kevin Gillette in a 2013 Post to golang-nuts. It implements 'erasure-style' generics by wrapping the generic implementation of each method with an adapter function which handles type assertions for the caller.

type Container struct {
    T SomeType

    Set func(value SomeType) SomeType

type Int64Container struct {
    T int64

    Set func(value int64) int64

func myProgram() {
    c := &Int64Container{}

Source code implementing a proof of concept can be found on Bitbucket. I can't really say I recommend anyone using it, but it's satisfying to show that it works!

Comments |

CSView - A fast viewer for very large CSV files
Sun, 23 Oct 2016 / projects / csview

CSView is a lightweight viewer that displays the start of a data file immediately so there's no waiting around for very large files to load. Using very little memory CSView can comfortably open files larger than 4GB.

With copy and paste functionality and selectable delimiter support CSView provides a fast, clean and simple way to access very large CSV files.

Updated October 2019 - Version 1.3.4 has been updated for compatibility with macOS Catalina. There are no new features in this release.

Updated June 2019 - Version 1.3.3 has a number of bugfixes: high-DPI rendering is fixed on Windows, and CSV files with empty columns no longer cause alignment problems.

Updated April 2019 - Version 1.3 now supports searching in files, high-DPI display resolutions and has improved rendering on MacOS Mojave.

Comments |

CSView 1.3.0 Screenshot

Backblaze B2 Performance Part 2
Sun, 13 Dec 2015 / projects / go / b2

After my earlier experiments with B2, I had an extremely interesting call with Backblaze about B2 features and performance.

Firstly, they have recently added a caching layer to speed up serving repeatedly requested files. This reduces the delay as the file is reassembled from Reed-Solomon slices. They also suggested that I do some new tests, as they thought I should be seeing faster speeds, even for first-access.

Comments |

Backblaze B2 and Go
Mon, 30 Nov 2015 / projects / go / b2

I've been implementing a Go client library for the Backblaze B2 cloud storage service: go-backblaze. It works extremely well, but is a bit slow to get files back.

Update 12th December: I've spoken to Backblaze, who have been working to improve performance. I have performed some new tests and written them up.

I've implemented a parallel download feature which you can use to download several files at the same time - this doesn't seem to affect the speed of individual downlaods very much, so I assume that the downloads are limited for one of two reasons

  1. There is a download speed cap in place
  2. The downloads each come from separate parts of the cluster file system, and so don't affect each other.

Downloading 5 copies of the same file in parallel doesn't seem to affect the download speed, nor do 5 sequential downloads. Whatever you do, each download seems to run at about 200KiB/sec.

Comments |

Sat, 10 Oct 2015 / projects

After reading this article about Magnetic Poetry I was taken by the idea that the fridge was no longer the central notice board of the home:

Our age-old impulses haven't gone away—changes in technology have just encouraged us to take them elsewhere. “It used to be the fridge door was the center of the home,” Kapell says. “That’s where people would hang their kids’ art, leave notes for each other, put pictures, all that stuff.”

So, I thought I would create a digital magnetic notice board, replete with fridge poetry, postcards and photos. I've got a few loose ends to tie up, but I'm pretty happy with the proof of concept.

Try it out! magnets.kothar.net

Comments |

Magnets screenshot

Wed, 13 May 2015 / projects

Languamatch is a service for locating language exchange partners based on shared interests and skill levels.


Comments |

Languamatch Chat

Thu, 7 May 2015 / projects

Furor is a web content curation tool for businesses.


Comments |


Organise Asana Projects
Thu, 26 Sep 2013 / projects / asana

UPDATE: Ditto - the successor to this tool - has now been launched. Please give it a go and let me know how you get on!

I was faced recently with wanting to move Asana projects from one workspace to another. Unfortunately this isn't a feature in the Asana interface (yet).

There's a PHP script floating around GitHub which will copy tasks between projects. This requires you to do some manual checking of project IDs, and you need to create the target project in advance, so I've extended the script and written a front-end web interface for it.

You can get the source code on BitBucket.

Visit asana.kothar.net to use the tool

Before you start, you may need to set up a few things that the tool can't do on its own though the API.

  1. Add your new user account (for your destination workspace) to your old workspace, if not already using the same email address
  2. Log in to Asana with your new account
  3. Add any accounts you want to keep task assignments for to the new workspace
  4. Add any custom fields you want to keep to the new workspace (pro workspaces)
  5. Log in to the tool: https://asana.kothar.net
  6. Choose the source workspace and project you’d like to copy
  7. Choose where you’d like to copy to
  8. Hit Go!


Copying Asana projects between workspaces

File sync and cloud storage client comparison
Fri, 26 Jul 2013 / blog / cloud storage

I've been trying out a number of file sync clients recently, finding that many are trying to solve the same problem and meeting with varying success. Probably the best-known service in this category is Dropbox, but nearly every big cloud services company, and a multitude of startups which base their services on top of them, are providing a 'cloud drive' of some description.

Comments |

Mon, 13 May 2013 / projects / slimtweet

SlimTweet tries to shorten a message by applying a set of rules. For example, and will be replaced by &, while it is will be replaced by it's.

Substitutions will be made one at a time until the message reaches a target length (140 characters by default).

If SlimTweet still can't make the message fit, it will start tweaking the Unicode characters which make up your message, without changing its appearance too much. For example, the digraph vi will be replaced by the roman numeral character . When rendered in a standard font, these substitutions are nearly inⅵsible.


Comments |