@@ -45,9 +45,54 @@ import (
4545 "bytes"
4646 "errors"
4747 "io"
48+ "runtime"
4849 "unsafe"
4950)
5051
52+ // PreparedDictionary is a handle to native object.
53+ type PreparedDictionary struct {
54+ opaque * C.BrotliEncoderPreparedDictionary
55+ pinner * runtime.Pinner
56+ }
57+
58+ // DictionaryType is type for shared dictionary
59+ type DictionaryType int
60+
61+ const (
62+ // DtRaw denotes LZ77 prefix dictionary
63+ DtRaw DictionaryType = 0
64+ // DtSerialized denotes serialized format
65+ DtSerialized DictionaryType = 1
66+ )
67+
68+ // NewPreparedDictionary prepares dictionary data for encoder.
69+ // Same instance can be used for multiple encoding sessions.
70+ // Close MUST be called to free resources.
71+ func NewPreparedDictionary (data []byte , dictionaryType DictionaryType , quality int ) * PreparedDictionary {
72+ var ptr * C.uint8_t
73+ if len (data ) != 0 {
74+ ptr = (* C .uint8_t )(& data [0 ])
75+ }
76+ p := new (runtime.Pinner )
77+ p .Pin (& data [0 ])
78+ d := C .BrotliEncoderPrepareDictionary (C .BrotliSharedDictionaryType (dictionaryType ), C .size_t (len (data )), ptr , C .int (quality ), nil , nil , nil )
79+ return & PreparedDictionary {
80+ opaque : d ,
81+ pinner : p ,
82+ }
83+ }
84+
85+ // Close frees C resources.
86+ // IMPORTANT: calling Close until all encoders that use that dictionary are closed as well will
87+ // cause crash.
88+ func (p * PreparedDictionary ) Close () error {
89+ // C-Brotli tolerates `nil` pointer here.
90+ C .BrotliEncoderDestroyPreparedDictionary (p .opaque )
91+ p .opaque = nil
92+ p .pinner .Unpin ()
93+ return nil
94+ }
95+
5196// WriterOptions configures Writer.
5297type WriterOptions struct {
5398 // Quality controls the compression-speed vs compression-density trade-offs.
@@ -56,38 +101,56 @@ type WriterOptions struct {
56101 // LGWin is the base 2 logarithm of the sliding window size.
57102 // Range is 10 to 24. 0 indicates automatic configuration based on Quality.
58103 LGWin int
104+ // Prepared shared dictionary
105+ Dictionary * PreparedDictionary
59106}
60107
61108// Writer implements io.WriteCloser by writing Brotli-encoded data to an
62109// underlying Writer.
63110type Writer struct {
111+ healthy bool
64112 dst io.Writer
65113 state * C.BrotliEncoderState
66114 buf , encoded []byte
67115}
68116
69117var (
70- errEncode = errors .New ("cbrotli: encode error" )
71- errWriterClosed = errors .New ("cbrotli: Writer is closed" )
118+ errEncode = errors .New ("cbrotli: encode error" )
119+ errWriterClosed = errors .New ("cbrotli: Writer is closed" )
120+ errWriterUnhealthy = errors .New ("cbrotli: Writer is unhealthy" )
72121)
73122
74123// NewWriter initializes new Writer instance.
75124// Close MUST be called to free resources.
76125func NewWriter (dst io.Writer , options WriterOptions ) * Writer {
77126 state := C .BrotliEncoderCreateInstance (nil , nil , nil )
78- C .BrotliEncoderSetParameter (
79- state , C .BROTLI_PARAM_QUALITY , (C .uint32_t )(options .Quality ))
127+ healthy := state != nil
128+ if C .BrotliEncoderSetParameter (
129+ state , C .BROTLI_PARAM_QUALITY , (C .uint32_t )(options .Quality )) == 0 {
130+ healthy = false
131+ }
80132 if options .LGWin > 0 {
81- C .BrotliEncoderSetParameter (
82- state , C .BROTLI_PARAM_LGWIN , (C .uint32_t )(options .LGWin ))
133+ if C .BrotliEncoderSetParameter (
134+ state , C .BROTLI_PARAM_LGWIN , (C .uint32_t )(options .LGWin )) == 0 {
135+ healthy = false
136+ }
137+ }
138+ if options .Dictionary != nil {
139+ if C .BrotliEncoderAttachPreparedDictionary (state , options .Dictionary .opaque ) == 0 {
140+ healthy = false
141+ }
83142 }
84143 return & Writer {
85- dst : dst ,
86- state : state ,
144+ healthy : healthy ,
145+ dst : dst ,
146+ state : state ,
87147 }
88148}
89149
90150func (w * Writer ) writeChunk (p []byte , op C.BrotliEncoderOperation ) (n int , err error ) {
151+ if ! w .healthy {
152+ return 0 , errWriterUnhealthy
153+ }
91154 if w .state == nil {
92155 return 0 , errWriterClosed
93156 }
0 commit comments