xref: /titanic_50/usr/src/lib/libast/common/sfio/sfseek.c (revision 275c9da86e89f8abf71135cf63d9fc23671b2e60)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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 /*	Set the IO pointer to a specific location in the stream
25 **
26 **	Written by Kiem-Phong Vo.
27 */
28 
29 #if __STD_C
30 static void newpos(Sfio_t* f, Sfoff_t p)
31 #else
32 static void newpos(f, p)
33 Sfio_t*	f;
34 Sfoff_t p;
35 #endif
36 {
37 #ifdef MAP_TYPE
38 	if((f->bits&SF_MMAP) && f->data)
39 	{	SFMUNMAP(f, f->data, f->endb-f->data);
40 		f->data = NIL(uchar*);
41 	}
42 #endif
43 	f->next = f->endr = f->endw = f->data;
44 	f->endb = (f->mode&SF_WRITE) ? f->data+f->size : f->data;
45 	if((f->here = p) < 0)
46 	{	f->extent = -1;
47 		f->here = 0;
48 	}
49 }
50 
51 #if __STD_C
52 Sfoff_t sfseek(Sfio_t* f, Sfoff_t p, int type)
53 #else
54 Sfoff_t sfseek(f,p,type)
55 Sfio_t*	f;	/* seek to a new location in this stream */
56 Sfoff_t	p;	/* place to seek to */
57 int	type;	/* 0: from org, 1: from here, 2: from end */
58 #endif
59 {
60 	Sfoff_t	r, s;
61 	int	mode, local, hardseek, mustsync;
62 
63 	SFMTXSTART(f, (Sfoff_t)(-1));
64 
65 	GETLOCAL(f,local);
66 
67 	hardseek = (type|f->flags)&(SF_SHARE|SF_PUBLIC);
68 
69 	if(hardseek && f->mode == (SF_READ|SF_SYNCED) )
70 	{	newpos(f,f->here);
71 		f->mode = SF_READ;
72 	}
73 
74 	/* set and initialize the stream to a definite mode */
75 	if((int)SFMODE(f,local) != (mode = f->mode&SF_RDWR))
76 	{	int	flags = f->flags;
77 
78 		if(hardseek&SF_PUBLIC) /* seek ptr must follow file descriptor */
79 			f->flags |= SF_SHARE|SF_PUBLIC;
80 		mode = _sfmode(f,mode,local);
81 		if(hardseek&SF_PUBLIC)
82 			f->flags = flags;
83 
84 		if(mode < 0)
85 			SFMTXRETURN(f, (Sfoff_t)(-1));
86 	}
87 
88 	mustsync = (type&SF_SHARE) && !(type&SF_PUBLIC) &&
89 		   (f->mode&SF_READ) && !(f->flags&SF_STRING);
90 
91 	/* Xopen-compliant */
92 	if((type &= (SEEK_SET|SEEK_CUR|SEEK_END)) != SEEK_SET &&
93 	   type != SEEK_CUR && type != SEEK_END )
94 	{	errno = EINVAL;
95 		SFMTXRETURN(f, (Sfoff_t)(-1));
96 	}
97 
98 	if(f->extent < 0)
99 	{	/* let system call set errno */
100 		(void)SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc);
101 		SFMTXRETURN(f, (Sfoff_t)(-1));
102 	}
103 
104 	/* throw away ungetc data */
105 	if(f->disc == _Sfudisc)
106 		(void)sfclose((*_Sfstack)(f,NIL(Sfio_t*)));
107 
108 	/* lock the stream for internal manipulations */
109 	SFLOCK(f,local);
110 
111 	/* clear error and eof bits */
112 	f->flags &= ~(SF_EOF|SF_ERROR);
113 
114 	while(f->flags&SF_STRING)
115 	{	SFSTRSIZE(f);
116 
117 		if(type == SEEK_CUR)
118 			r = p + (f->next - f->data);
119 		else if(type == SEEK_END)
120 			r = p + f->extent;
121 		else	r = p;
122 
123 		if(r >= 0 && r <= f->size)
124 		{	p = r;
125 			f->next = f->data+p;
126 			f->here = p;
127 			if(p > f->extent)
128 				memclear((char*)(f->data+f->extent),(int)(p-f->extent));
129 			goto done;
130 		}
131 
132 		/* check exception handler, note that this may pop stream */
133 		if(SFSK(f,r,SEEK_SET,f->disc) != r)
134 		{	p = -1;
135 			goto done;
136 		}
137 		else if(!(f->flags&SF_STRING))
138 		{	p = r;
139 			goto done;
140 		}
141 	}
142 
143 	if(f->mode&SF_WRITE)
144 	{	/* see if we can avoid flushing buffer */
145 		if(!hardseek && type < SEEK_END && !(f->flags&SF_APPENDWR) )
146 		{	s = f->here + (f->next - f->data);
147 			r = p + (type == SEEK_SET ? 0 : s);
148 			if(r == s)
149 			{	p = r;
150 				goto done;
151 			}
152 		}
153 
154 		if(f->next > f->data && SFSYNC(f) < 0)
155 		{	p = -1;
156 			goto done;
157 		}
158 	}
159 
160 	if(type == SEEK_END || (f->mode&SF_WRITE) )
161 	{	if((hardseek&SF_PUBLIC) || type == SEEK_END)
162 			p = SFSK(f, p, type, f->disc);
163 		else
164 		{	r = p + (type == SEEK_CUR ? f->here : 0);
165 			p = (hardseek || r != f->here) ? SFSK(f,r,SEEK_SET,f->disc) : r;
166 		}
167 		if(p >= 0)
168 			newpos(f,p);
169 
170 		goto done;
171 	}
172 
173 	/* if get here, must be a read stream */
174 	s = f->here - (f->endb - f->next);
175 	r = p + (type == SEEK_CUR ? s : 0);
176 	if(r <= f->here && r >= (f->here - (f->endb-f->data)) )
177 	{	if((hardseek || (type == SEEK_CUR && p == 0)) )
178 		{	if((s = SFSK(f, (Sfoff_t)0, SEEK_CUR, f->disc)) == f->here ||
179 			   (s >= 0 && !(hardseek&SF_PUBLIC) &&
180 			    (s = SFSK(f, f->here, SEEK_SET, f->disc)) == f->here) )
181 				goto near_done;
182 			else if(s < 0)
183 			{	p = -1;
184 				goto done;
185 			}
186 			else
187 			{	newpos(f,s);
188 				hardseek = 0;
189 			}
190 		}
191 		else
192 		{ near_done:
193 			f->next = f->endb - (f->here - r);
194 			p = r;
195 			goto done;
196 		}
197 	}
198 
199 	/* desired position */
200 	if((p += type == SEEK_CUR ? s : 0) < 0)
201 		goto done;
202 
203 #ifdef MAP_TYPE
204 	if(f->bits&SF_MMAP)
205 	{	/* if mmap is not great, stop mmaping if moving around too much */
206 #if _mmap_worthy < 2
207 		if((f->next - f->data) < ((f->endb - f->data)/4) )
208 		{	SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
209 			hardseek = 1; /* this forces a hard seek below */
210 		}
211 		else
212 #endif
213 		{	/* for mmap, f->here can be virtual except for hardseek */
214 			newpos(f,p);
215 			if(!hardseek)
216 				goto done;
217 		}
218 	}
219 #endif
220 
221 	if(f->endb > f->next)
222 	{	/* reduce wastage in future buffer fillings */
223 		f->iosz = (f->next - f->data) + (f->endb - f->next)/2;
224 		f->iosz = ((f->iosz + f->blksz-1)/f->blksz)*f->blksz;
225 	}
226 	if(f->iosz >= f->size)
227 		f->iosz = 0;
228 
229 	/* buffer is now considered empty */
230 	f->next = f->endr = f->endb = f->data;
231 
232 	/* small backseeks often come in bunches, so seek back as far as possible */
233 	if(p < f->lpos && f->size > f->blksz && (p + f->blksz) > s)
234 	{	if((r = s - f->size) < 0)
235 			r = 0;
236 	}
237 	/* try to align buffer to block boundary to enhance I/O speed */
238 	else if(f->blksz > 0 && f->size >= 2*f->blksz)
239 		r = p - (p%f->blksz);
240 	else
241 	{	r = p;
242 
243 		/* seeking around and wasting data, be conservative */
244 		if(f->iosz > 0 && (p > f->lpos || p < f->lpos-f->size) )
245 			f->bits |= SF_JUSTSEEK;
246 	}
247 
248 	if((hardseek || r != f->here) && (f->here = SFSK(f,r,SEEK_SET,f->disc)) != r)
249 	{	if(r < p) /* now try to just get to p */
250 			f->here = SFSK(f,p,SEEK_SET,f->disc);
251 		if(f->here != p)
252 			p = -1;
253 		goto done;
254 	}
255 
256 	if(r < p) /* read to cover p */
257 	{	(void)SFRD(f, f->data, f->size, f->disc);
258 		if(p <= f->here && p >= (f->here - (f->endb - f->data)) )
259 			f->next = f->endb - (size_t)(f->here-p);
260 		else /* recover from read failure by just seeking to p */
261 		{	f->next = f->endb = f->data;
262 			if((f->here = SFSK(f,p,SEEK_SET,f->disc)) != p)
263 				p = -1;
264 		}
265 	}
266 
267 done :
268 	if(f->here < 0) /* hasn't been the best of time */
269 	{	f->extent = -1;
270 		f->here = 0;
271 	}
272 
273 	f->lpos = p;
274 
275 	SFOPEN(f,local);
276 
277 	if(mustsync)
278 		sfsync(f);
279 	SFMTXRETURN(f, p);
280 }
281