1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2013 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * David Korn <dgkorn@gmail.com> *
19 * Phong Vo <phongvo@gmail.com> *
20 * *
21 ***********************************************************************/
22 #include "sfhdr.h"
23
24 /* Move data from one stream to another.
25 ** This code is written so that it'll work even in the presence
26 ** of stacking streams, pool, and discipline.
27 ** If you must change it, be gentle.
28 **
29 ** Written by Kiem-Phong Vo.
30 */
31 #define MAX_SSIZE ((ssize_t)((~((size_t)0)) >> 1))
32
33 #if __STD_C
sfmove(Sfio_t * fr,Sfio_t * fw,Sfoff_t n,reg int rc)34 Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, reg int rc)
35 #else
36 Sfoff_t sfmove(fr,fw,n,rc)
37 Sfio_t* fr; /* moving data from this stream */
38 Sfio_t* fw; /* moving data to this stream */
39 Sfoff_t n; /* number of bytes/records to move. <0 for unbounded move */
40 reg int rc; /* record separator */
41 #endif
42 {
43 reg uchar *cp, *next;
44 reg ssize_t r, w;
45 reg uchar *endb;
46 reg int direct;
47 Sfoff_t n_move, sk, cur;
48 uchar *rbuf = NIL(uchar*);
49 ssize_t rsize = 0;
50 SFMTXDECL(fr); /* declare a shadow stream variable for from stream */
51 SFMTXDECL2(fw); /* declare a shadow stream variable for to stream */
52
53 SFMTXENTER(fr, (Sfoff_t)0);
54 if(fw)
55 SFMTXBEGIN2(fw, (Sfoff_t)0);
56
57 for(n_move = 0; n != 0; )
58 {
59 if(rc >= 0) /* moving records, let sfgetr() deal with record reading */
60 { if(!(cp = (uchar*)sfgetr(fr,rc,0)) )
61 n = 0;
62 else
63 { r = sfvalue(fr);
64 if(fw && (w = SFWRITE(fw, cp, r)) != r)
65 { if(fr->extent >= 0 )
66 (void)SFSEEK(fr,(Sfoff_t)(-r),SEEK_CUR);
67 if(fw->extent >= 0 && w > 0)
68 (void)SFSEEK(fw,(Sfoff_t)(-w),SEEK_CUR);
69 n = 0;
70 }
71 else
72 { n_move += 1;
73 if(n > 0)
74 n -= 1;
75 }
76 }
77 continue;
78 }
79
80 /* get the streams into the right mode */
81 if(fr->mode != SF_READ && _sfmode(fr,SF_READ,0) < 0)
82 break;
83
84 SFLOCK(fr,0);
85
86 /* flush the write buffer as necessary to make room */
87 if(fw)
88 { if(fw->mode != SF_WRITE && _sfmode(fw,SF_WRITE,0) < 0 )
89 break;
90 SFLOCK(fw,0);
91 if(fw->next >= fw->endb ||
92 (fw->next > fw->data && fr->extent < 0 &&
93 (fw->extent < 0 || (fw->flags&SF_SHARE)) ) )
94 if(SFFLSBUF(fw,-1) < 0 )
95 break;
96 }
97 else if((cur = SFSEEK(fr, (Sfoff_t)0, SEEK_CUR)) >= 0 )
98 { sk = n > 0 ? SFSEEK(fr, n, SEEK_CUR) : SFSEEK(fr, 0, SEEK_END);
99 if(sk > cur) /* safe to skip over data in current stream */
100 { n_move += sk - cur;
101 if(n > 0)
102 n -= sk - cur;
103 continue;
104 }
105 /* else: stream unstacking may happen below */
106 }
107
108 /* about to move all, set map to a large amount */
109 if(n < 0 && (fr->bits&SF_MMAP) && !(fr->bits&SF_MVSIZE) )
110 { SFMVSET(fr);
111 fr->bits |= SF_SEQUENTIAL; /* sequentially access data */
112 }
113
114 /* try reading a block of data */
115 direct = 0;
116 if(fr->rsrv && (r = -fr->rsrv->slen) > 0)
117 { fr->rsrv->slen = 0;
118 next = fr->rsrv->data;
119 }
120 else if((r = fr->endb - (next = fr->next)) <= 0)
121 { /* amount of data remained to be read */
122 if((w = n > MAX_SSIZE ? MAX_SSIZE : (ssize_t)n) < 0)
123 { if(fr->extent < 0)
124 w = fr->data == fr->tiny ? SF_GRAIN : fr->size;
125 else if((fr->extent-fr->here) > SF_NMAP*SF_PAGE)
126 w = SF_NMAP*SF_PAGE;
127 else w = (ssize_t)(fr->extent-fr->here);
128 }
129
130 /* use a decent buffer for data transfer but make sure
131 that if we overread, the left over can be retrieved
132 */
133 if(!(fr->flags&SF_STRING) && !(fr->bits&SF_MMAP) &&
134 (n < 0 || fr->extent >= 0) )
135 { reg ssize_t maxw = 4*(_Sfpage > 0 ? _Sfpage : SF_PAGE);
136
137 /* direct transfer to a seekable write stream */
138 if(fw && fw->extent >= 0 && w <= (fw->endb-fw->next) )
139 { w = fw->endb - (next = fw->next);
140 direct = SF_WRITE;
141 }
142 else if(w > fr->size && maxw > fr->size)
143 { /* making our own buffer */
144 if(w >= maxw)
145 w = maxw;
146 else w = ((w+fr->size-1)/fr->size)*fr->size;
147 if(rsize <= 0 && (rbuf = (uchar*)malloc(w)) )
148 rsize = w;
149 if(rbuf)
150 { next = rbuf;
151 w = rsize;
152 direct = SF_STRING;
153 }
154 }
155 }
156
157 if(!direct)
158 { /* make sure we don't read too far ahead */
159 if(n > 0 && fr->extent < 0 && (fr->flags&SF_SHARE) )
160 { if((Sfoff_t)(r = fr->size) > n)
161 r = (ssize_t)n;
162 }
163 else r = -1;
164 if((r = SFFILBUF(fr,r)) <= 0)
165 break;
166 next = fr->next;
167 }
168 else
169 { /* actual amount to be read */
170 if(n > 0 && n < w)
171 w = (ssize_t)n;
172
173 if((r = SFRD(fr,next,w,fr->disc)) > 0)
174 fr->next = fr->endb = fr->endr = fr->data;
175 else if(r == 0)
176 break; /* eof */
177 else goto again; /* popped stack */
178 }
179 }
180
181 /* compute the extent of data to be moved */
182 endb = next+r;
183 if(n > 0)
184 { if(r > n)
185 r = (ssize_t)n;
186 n -= r;
187 }
188 n_move += r;
189 cp = next+r;
190
191 if(!direct)
192 fr->next += r;
193 else if((w = endb-cp) > 0)
194 { /* move left-over to read stream */
195 if(w > fr->size)
196 w = fr->size;
197 memcpy((Void_t*)fr->data,(Void_t*)cp,w);
198 fr->endb = fr->data+w;
199 if((w = endb - (cp+w)) > 0)
200 (void)SFSK(fr,(Sfoff_t)(-w),SEEK_CUR,fr->disc);
201 }
202
203 if(fw)
204 { if(direct == SF_WRITE)
205 fw->next += r;
206 else if(r <= (fw->endb-fw->next) )
207 { memcpy((Void_t*)fw->next,(Void_t*)next,r);
208 fw->next += r;
209 }
210 else if((w = SFWRITE(fw,(Void_t*)next,r)) != r)
211 { /* a write error happened */
212 if(w > 0)
213 { r -= w;
214 n_move -= r;
215 }
216 if(fr->extent >= 0)
217 (void)SFSEEK(fr,(Sfoff_t)(-r),SEEK_CUR);
218 break;
219 }
220 }
221
222 again:
223 SFOPEN(fr,0);
224 if(fw)
225 SFOPEN(fw,0);
226 }
227
228 if(n < 0 && (fr->bits&SF_MMAP) && (fr->bits&SF_MVSIZE))
229 { /* back to normal access mode */
230 SFMVUNSET(fr);
231 if((fr->bits&SF_SEQUENTIAL) && (fr->data))
232 SFMMSEQOFF(fr,fr->data,fr->endb-fr->data);
233 fr->bits &= ~SF_SEQUENTIAL;
234 }
235
236 if(rbuf)
237 free(rbuf);
238
239 if(fw)
240 { SFOPEN(fw,0);
241 SFMTXEND2(fw);
242 }
243
244 SFOPEN(fr,0);
245 SFMTXRETURN(fr, n_move);
246 }
247