]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | // Copyright 2011 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | package norm | |
6 | ||
7 | import "io" | |
8 | ||
9 | type normWriter struct { | |
10 | rb reorderBuffer | |
11 | w io.Writer | |
12 | buf []byte | |
13 | } | |
14 | ||
15 | // Write implements the standard write interface. If the last characters are | |
16 | // not at a normalization boundary, the bytes will be buffered for the next | |
17 | // write. The remaining bytes will be written on close. | |
18 | func (w *normWriter) Write(data []byte) (n int, err error) { | |
19 | // Process data in pieces to keep w.buf size bounded. | |
20 | const chunk = 4000 | |
21 | ||
22 | for len(data) > 0 { | |
23 | // Normalize into w.buf. | |
24 | m := len(data) | |
25 | if m > chunk { | |
26 | m = chunk | |
27 | } | |
28 | w.rb.src = inputBytes(data[:m]) | |
29 | w.rb.nsrc = m | |
30 | w.buf = doAppend(&w.rb, w.buf, 0) | |
31 | data = data[m:] | |
32 | n += m | |
33 | ||
34 | // Write out complete prefix, save remainder. | |
35 | // Note that lastBoundary looks back at most 31 runes. | |
36 | i := lastBoundary(&w.rb.f, w.buf) | |
37 | if i == -1 { | |
38 | i = 0 | |
39 | } | |
40 | if i > 0 { | |
41 | if _, err = w.w.Write(w.buf[:i]); err != nil { | |
42 | break | |
43 | } | |
44 | bn := copy(w.buf, w.buf[i:]) | |
45 | w.buf = w.buf[:bn] | |
46 | } | |
47 | } | |
48 | return n, err | |
49 | } | |
50 | ||
51 | // Close forces data that remains in the buffer to be written. | |
52 | func (w *normWriter) Close() error { | |
53 | if len(w.buf) > 0 { | |
54 | _, err := w.w.Write(w.buf) | |
55 | if err != nil { | |
56 | return err | |
57 | } | |
58 | } | |
59 | return nil | |
60 | } | |
61 | ||
62 | // Writer returns a new writer that implements Write(b) | |
107c1cdb ND |
63 | // by writing f(b) to w. The returned writer may use an |
64 | // internal buffer to maintain state across Write calls. | |
15c0b25d AP |
65 | // Calling its Close method writes any buffered data to w. |
66 | func (f Form) Writer(w io.Writer) io.WriteCloser { | |
67 | wr := &normWriter{rb: reorderBuffer{}, w: w} | |
68 | wr.rb.init(f, nil) | |
69 | return wr | |
70 | } | |
71 | ||
72 | type normReader struct { | |
73 | rb reorderBuffer | |
74 | r io.Reader | |
75 | inbuf []byte | |
76 | outbuf []byte | |
77 | bufStart int | |
78 | lastBoundary int | |
79 | err error | |
80 | } | |
81 | ||
82 | // Read implements the standard read interface. | |
83 | func (r *normReader) Read(p []byte) (int, error) { | |
84 | for { | |
85 | if r.lastBoundary-r.bufStart > 0 { | |
86 | n := copy(p, r.outbuf[r.bufStart:r.lastBoundary]) | |
87 | r.bufStart += n | |
88 | if r.lastBoundary-r.bufStart > 0 { | |
89 | return n, nil | |
90 | } | |
91 | return n, r.err | |
92 | } | |
93 | if r.err != nil { | |
94 | return 0, r.err | |
95 | } | |
96 | outn := copy(r.outbuf, r.outbuf[r.lastBoundary:]) | |
97 | r.outbuf = r.outbuf[0:outn] | |
98 | r.bufStart = 0 | |
99 | ||
100 | n, err := r.r.Read(r.inbuf) | |
101 | r.rb.src = inputBytes(r.inbuf[0:n]) | |
102 | r.rb.nsrc, r.err = n, err | |
103 | if n > 0 { | |
104 | r.outbuf = doAppend(&r.rb, r.outbuf, 0) | |
105 | } | |
106 | if err == io.EOF { | |
107 | r.lastBoundary = len(r.outbuf) | |
108 | } else { | |
109 | r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf) | |
110 | if r.lastBoundary == -1 { | |
111 | r.lastBoundary = 0 | |
112 | } | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | // Reader returns a new reader that implements Read | |
118 | // by reading data from r and returning f(data). | |
119 | func (f Form) Reader(r io.Reader) io.Reader { | |
120 | const chunk = 4000 | |
121 | buf := make([]byte, chunk) | |
122 | rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf} | |
123 | rr.rb.init(f, buf) | |
124 | return rr | |
125 | } |