<commit_before>package rest

import (
    "bytes"
    "crypto/md5"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
    "time"
    "sync"
    "github.com/johnny-morrice/godelbrot/process"
    lib "github.com/johnny-morrice/godelbrot/libgodelbrot"
)

type rqstate uint8

const (
    __WAIT = rqstate(iota)
    __DONE
    __ERROR
)

type rqitem struct {
    createtime time.Time
    completetime time.Time
    pkt renderpacket
    code hashcode
    state rqstate
    err string
    nextinfo lib.Info
    mutex sync.RWMutex
}

func makeRqitem(pkt *renderpacket) *rqitem {
    rqi := &rqitem{}
    rqi.pkt = *pkt
    rqi.createtime = time.Now()

    buff := &bytes.Buffer{}
    enc := json.NewEncoder(buff)
    err := enc.Encode(pkt)
    if err != nil {
        panic("renderpacket should serialize")
    }
    hsh := md5.Sum(buff.Bytes())
    buff64 := &bytes.Buffer{}
    enc64 := base64.NewEncoder(base64.URLEncoding, buff64)
    enc64.Write(hsh[:])
    rqi.code = hashcode(buff64.String())
    return rqi
}

func (rqi *rqitem) packet() renderpacket {
    rqi.mutex.RLock()
    defer rqi.mutex.RUnlock()
    return rqi.pkt
}

func (rqi *rqitem) hash() hashcode {
    rqi.mutex.RLock()
    defer rqi.mutex.RUnlock()
    return rqi.code
}

func (rqi *rqitem) done(nextinfo *lib.Info) {
    writeM(rqi.mutex, func () {
        rqi.nextinfo = *nextinfo
        rqi.state = __DONE
    })
    rqi.logcomplete()
}

func (rqi *rqitem) fail(msg string) {
    writeM(rqi.mutex, func () {
        rqi.err = msg
        rqi.state = __ERROR
    })
    rqi.logcomplete()
}

func (rqi *rqitem) logcomplete() {
    var state rqstate
    var milli time.Duration
    var pkt renderpacket
    var err string
    writeM(rqi.mutex, func () {
        rqi.completetime = time.Now()
        elapsed := rqi.completetime.Sub(rqi.createtime)
        milli = elapsed * time.Millisecond
        state = rqi.state
        pkt = rqi.pkt
        err = rqi.err
    })
    switch state {
    case __DONE:
        log.Printf("rqitem rendered OK after %v milliseconds", milli)
    case __ERROR:
        log.Printf("rqitem error after %v milliseconds: %v", milli, err)
    default:
        panic(fmt.Sprintf("rq completed after %v milliseconds with bad state (%v): %v",
                milli, state, pkt))
    }
}
<commit_msg>Fix duplicate rqitem hashcode bug.<commit_after>package rest

import (
    "bytes"
    "crypto/md5"
    "encoding/base64"
    "encoding/gob"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "time"
    "sync"
    "github.com/johnny-morrice/godelbrot/process"
    lib "github.com/johnny-morrice/godelbrot/libgodelbrot"
)

type rqstate uint8

const (
    __WAIT = rqstate(iota)
    __DONE
    __ERROR
)

type rqitem struct {
    createtime time.Time
    completetime time.Time
    pkt renderpacket
    code hashcode
    state rqstate
    err string
    nextinfo lib.Info
    mutex sync.RWMutex
}

func makeRqitem(pkt *renderpacket) *rqitem {
    rqi := &rqitem{}
    rqi.pkt = *pkt
    rqi.createtime = time.Now()

    type UserPacket struct {
        WantZoom bool
        Target lib.ZoomTarget
        Info lib.UserInfo
    }
    userpkt := UserPacket{
        WantZoom: pkt.wantzoom,
        Target: pkt.target,
        Info: *lib.Friendly(&pkt.info),
    }

    // Use json in debug mode but gob otherwise.
    serialize := func (encfactory func (io.Writer) func(interface{}) error) []byte {
        buff := &bytes.Buffer{}
        encoder := encfactory(buff)
        err := encoder(userpkt)
        if err != nil {
            panic(fmt.Sprintf("Render packet should serialize: %v", err))
        }
        return buff.Bytes()
    }
    var dat []byte
    if __DEBUG {
        dat = serialize(func (w io.Writer) func(interface{}) error {
            enc := json.NewEncoder(w)
            return enc.Encode
        })
    } else {
        dat = serialize(func (w io.Writer) func (interface{}) error {
            enc := gob.NewEncoder(w)
            return enc.Encode
        })
    }
    hsh := md5.Sum(dat)
    buff64 := &bytes.Buffer{}
    enc64 := base64.NewEncoder(base64.URLEncoding, buff64)
    enc64.Write(hsh[:])
    rqi.code = hashcode(buff64.String())

    debugf("rqi with packet representation %v has code %v", string(dat), rqi.code)
    return rqi
}

func (rqi *rqitem) packet() renderpacket {
    rqi.mutex.RLock()
    defer rqi.mutex.RUnlock()
    return rqi.pkt
}

func (rqi *rqitem) hash() hashcode {
    rqi.mutex.RLock()
    defer rqi.mutex.RUnlock()
    return rqi.code
}

func (rqi *rqitem) done(nextinfo *lib.Info) {
    writeM(rqi.mutex, func () {
        rqi.nextinfo = *nextinfo
        rqi.state = __DONE
    })
    rqi.logcomplete()
}

func (rqi *rqitem) fail(msg string) {
    writeM(rqi.mutex, func () {
        rqi.err = msg
        rqi.state = __ERROR
    })
    rqi.logcomplete()
}

func (rqi *rqitem) logcomplete() {
    var state rqstate
    var elapsed time.Duration
    var pkt renderpacket
    var err string
    writeM(rqi.mutex, func () {
        rqi.completetime = time.Now()
        elapsed = rqi.completetime.Sub(rqi.createtime)
        state = rqi.state
        pkt = rqi.pkt
        err = rqi.err
    })
    switch state {
    case __DONE:
        log.Printf("rqitem rendered OK after %v", elapsed)
    case __ERROR:
        log.Printf("rqitem error after %v: %v", elapsed, err)
    default:
        panic(fmt.Sprintf("rq completed after %v with bad state (%v): %v",
                elapsed, state, pkt))
    }
}

{'fraction_non_alphanumeric': 0.12060761346998536, 'fraction_numerical': 0.006954612005856516, 'mean_word_length': 2.274415817855003, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1159765', 'n_tokens_mistral': 1978, 'n_tokens_neox': 1912, 'n_words': 445}