xref: /titanic_50/usr/src/lib/libast/common/sfio/sfmove.c (revision b97d6ca7333c353b6ca20c20c99fb1be8d32a8de)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.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
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);
51 	SFMTXDECL2(fw);
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((r = fr->endb - (next = fr->next)) <= 0)
117 		{	/* amount of data remained to be read */
118 			if((w = n > MAX_SSIZE ? MAX_SSIZE : (ssize_t)n) < 0)
119 			{	if(fr->extent < 0)
120 					w = fr->data == fr->tiny ? SF_GRAIN : fr->size;
121 				else if((fr->extent-fr->here) > SF_NMAP*SF_PAGE)
122 					w = SF_NMAP*SF_PAGE;
123 				else	w = (ssize_t)(fr->extent-fr->here);
124 			}
125 
126 			/* use a decent buffer for data transfer but make sure
127 			   that if we overread, the left over can be retrieved
128 			*/
129 			if(!(fr->flags&SF_STRING) && !(fr->bits&SF_MMAP) &&
130 			   (n < 0 || fr->extent >= 0) )
131 			{	reg ssize_t maxw = 4*(_Sfpage > 0 ? _Sfpage : SF_PAGE);
132 
133 				/* direct transfer to a seekable write stream */
134 				if(fw && fw->extent >= 0 && w <= (fw->endb-fw->next) )
135 				{	w = fw->endb - (next = fw->next);
136 					direct = SF_WRITE;
137 				}
138 				else if(w > fr->size && maxw > fr->size)
139 				{	/* making our own buffer */
140 					if(w >= maxw)
141 						w = maxw;
142 					else	w = ((w+fr->size-1)/fr->size)*fr->size;
143 					if(rsize <= 0 && (rbuf = (uchar*)malloc(w)) )
144 						rsize = w;
145 					if(rbuf)
146 					{	next = rbuf;
147 						w = rsize;
148 						direct = SF_STRING;
149 					}
150 				}
151 			}
152 
153 			if(!direct)
154 			{	/* make sure we don't read too far ahead */
155 				if(n > 0 && fr->extent < 0 && (fr->flags&SF_SHARE) )
156 				{	if((Sfoff_t)(r = fr->size) > n)
157 						r = (ssize_t)n;
158 				}
159 				else	r = -1;
160 				if((r = SFFILBUF(fr,r)) <= 0)
161 					break;
162 				next = fr->next;
163 			}
164 			else
165 			{	/* actual amount to be read */
166 				if(n > 0 && n < w)
167 					w = (ssize_t)n;
168 
169 				if((r = SFRD(fr,next,w,fr->disc)) > 0)
170 					fr->next = fr->endb = fr->endr = fr->data;
171 				else if(r == 0)
172 					break;		/* eof */
173 				else	goto again;	/* popped stack */
174 			}
175 		}
176 
177 		/* compute the extent of data to be moved */
178 		endb = next+r;
179 		if(n > 0)
180 		{	if(r > n)
181 				r = (ssize_t)n;
182 			n -= r;
183 		}
184 		n_move += r;
185 		cp = next+r;
186 
187 		if(!direct)
188 			fr->next += r;
189 		else if((w = endb-cp) > 0)
190 		{	/* move left-over to read stream */
191 			if(w > fr->size)
192 				w = fr->size;
193 			memcpy((Void_t*)fr->data,(Void_t*)cp,w);
194 			fr->endb = fr->data+w;
195 			if((w = endb - (cp+w)) > 0)
196 				(void)SFSK(fr,(Sfoff_t)(-w),SEEK_CUR,fr->disc);
197 		}
198 
199 		if(fw)
200 		{	if(direct == SF_WRITE)
201 				fw->next += r;
202 			else if(r <= (fw->endb-fw->next) )
203 			{	memcpy((Void_t*)fw->next,(Void_t*)next,r);
204 				fw->next += r;
205 			}
206 			else if((w = SFWRITE(fw,(Void_t*)next,r)) != r)
207 			{	/* a write error happened */
208 				if(w > 0)
209 				{	r -= w;
210 					n_move -= r;
211 				}
212 				if(fr->extent >= 0)
213 					(void)SFSEEK(fr,(Sfoff_t)(-r),SEEK_CUR);
214 				break;
215 			}
216 		}
217 
218 	again:
219 		SFOPEN(fr,0);
220 		if(fw)
221 			SFOPEN(fw,0);
222 	}
223 
224 	if(n < 0 && (fr->bits&SF_MMAP) && (fr->bits&SF_MVSIZE))
225 	{	/* back to normal access mode */
226 		SFMVUNSET(fr);
227 		if((fr->bits&SF_SEQUENTIAL) && (fr->data))
228 			SFMMSEQOFF(fr,fr->data,fr->endb-fr->data);
229 		fr->bits &= ~SF_SEQUENTIAL;
230 	}
231 
232 	if(rbuf)
233 		free(rbuf);
234 
235 	if(fw)
236 	{	SFOPEN(fw,0);
237 		SFMTXEND2(fw);
238 	}
239 
240 	SFOPEN(fr,0);
241 	SFMTXRETURN(fr, n_move);
242 }
243