3 Copyright (C) 2005 and later Cockos Incorporated
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 This file provides the WDL_FileWrite object, which can be used to create/write files.
23 On windows systems it supports writing synchronously, asynchronously, and asynchronously without buffering.
24 On windows systems it supports files larger than 4gb.
25 On non-windows systems it acts as a wrapper for fopen()/etc.
31 #ifndef _WDL_FILEWRITE_H_
32 #define _WDL_FILEWRITE_H_
41 #if defined(_WIN32) && !defined(WDL_NO_WIN32_FILEWRITE)
42 #ifndef WDL_WIN32_NATIVE_WRITE
43 #define WDL_WIN32_NATIVE_WRITE
46 #ifdef WDL_WIN32_NATIVE_WRITE
47 #undef WDL_WIN32_NATIVE_WRITE
49 #if !defined(WDL_NO_POSIX_FILEWRITE)
50 #include <sys/fcntl.h>
53 #include <sys/errno.h>
54 #define WDL_POSIX_NATIVE_WRITE
61 #define WDL_FILEWRITE_POSTYPE __int64
63 #define WDL_FILEWRITE_POSTYPE long long
66 //#define WIN32_ASYNC_NOBUF_WRITE // this doesnt seem to give much perf increase (writethrough with buffering is fine, since ultimately writes get deferred anyway)
70 #ifdef WDL_WIN32_NATIVE_WRITE
72 class WDL_FileWrite__WriteEnt
75 WDL_FileWrite__WriteEnt(int sz
)
80 m_bufptr
= (char *)__buf
.Resize(sz
+4095);
81 int a
=((int)(INT_PTR
)m_bufptr
)&4095;
82 if (a
) m_bufptr
+= 4096-a
;
84 memset(&m_ol
,0,sizeof(m_ol
));
85 m_ol
.hEvent
=CreateEvent(NULL
,TRUE
,TRUE
,NULL
);
87 ~WDL_FileWrite__WriteEnt()
89 CloseHandle(m_ol
.hEvent
);
92 WDL_FILEWRITE_POSTYPE m_last_writepos
;
94 int m_bufused
,m_bufsz
;
97 WDL_TypedBuf
<char> __buf
;
102 #if defined(_WIN32) && !defined(WDL_NO_SUPPORT_UTF8)
103 BOOL
HasUTF8(const char *_str
)
105 const unsigned char *str
= (const unsigned char *)_str
;
106 if (!str
) return FALSE
;
109 unsigned char c
= *str
;
112 if (c
<= 0xDF && str
[1] >=0x80 && str
[1] <= 0xBF) return TRUE
;
113 else if (c
<= 0xEF && str
[1] >=0x80 && str
[1] <= 0xBF && str
[2] >=0x80 && str
[2] <= 0xBF) return TRUE
;
114 else if (c
<= 0xF4 && str
[1] >=0x80 && str
[1] <= 0xBF && str
[2] >=0x80 && str
[2] <= 0xBF) return TRUE
;
124 WDL_FileWrite(const char *filename
, int allow_async
=1, int bufsize
=8192, int minbufs
=16, int maxbufs
=16, bool wantAppendTo
=false, bool noFileLocking
=false) // async==2 is unbuffered
127 m_file_max_position
=0;
130 #ifdef WDL_WIN32_NATIVE_WRITE
131 m_fh
= INVALID_HANDLE_VALUE
;
133 #elif defined(WDL_POSIX_NATIVE_WRITE)
134 m_filedes_locked
=false;
143 #ifdef WDL_WIN32_NATIVE_WRITE
144 #ifdef WDL_SUPPORT_WIN9X
145 const bool isNT
= (GetVersion()<0x80000000);
147 const bool isNT
= true;
149 m_async
= allow_async
&& isNT
;
150 #ifdef WIN32_ASYNC_NOBUF_WRITE
151 bufsize
= (bufsize
+4095)&~4095;
152 if (bufsize
<4096) bufsize
=4096;
155 int rwflag
= GENERIC_WRITE
;
156 int createFlag
= wantAppendTo
?OPEN_ALWAYS
:CREATE_ALWAYS
;
157 int shareFlag
= noFileLocking
? (FILE_SHARE_READ
|FILE_SHARE_WRITE
) : FILE_SHARE_READ
;
158 int flag
= FILE_ATTRIBUTE_NORMAL
;
162 rwflag
|= GENERIC_READ
;
163 #ifdef WIN32_ASYNC_NOBUF_WRITE
164 flag
|= FILE_FLAG_OVERLAPPED
|FILE_FLAG_NO_BUFFERING
|FILE_FLAG_WRITE_THROUGH
;
166 flag
|= FILE_FLAG_OVERLAPPED
|(allow_async
>1 ? FILE_FLAG_WRITE_THROUGH
: 0);
171 #ifndef WDL_NO_SUPPORT_UTF8
172 m_fh
=INVALID_HANDLE_VALUE
;
173 if (isNT
&& HasUTF8(filename
))
175 int szreq
=MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,filename
,-1,NULL
,0);
178 WDL_TypedBuf
<WCHAR
> wfilename
;
179 wfilename
.Resize(szreq
+10);
180 if (MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,filename
,-1,wfilename
.Get(),wfilename
.GetSize()))
181 m_fh
= CreateFileW(wfilename
.Get(),rwflag
,shareFlag
,NULL
,createFlag
,flag
,NULL
);
185 WCHAR wfilename
[1024];
186 if (MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,filename
,-1,wfilename
,1024))
187 m_fh
= CreateFileW(wfilename
,rwflag
,shareFlag
,NULL
,createFlag
,flag
,NULL
);
191 if (m_fh
== INVALID_HANDLE_VALUE
)
193 m_fh
= CreateFileA(filename
,rwflag
,shareFlag
,NULL
,createFlag
,flag
,NULL
);
196 if (m_async
&& m_fh
!= INVALID_HANDLE_VALUE
)
198 m_async_bufsize
=bufsize
;
199 m_async_maxbufs
=maxbufs
;
200 m_async_minbufs
=minbufs
;
202 for (x
= 0; x
< m_async_minbufs
; x
++)
204 WDL_FileWrite__WriteEnt
*t
=new WDL_FileWrite__WriteEnt(m_async_bufsize
);
209 if (m_fh
!= INVALID_HANDLE_VALUE
&& wantAppendTo
)
210 SetPosition(GetSize());
212 #elif defined(WDL_POSIX_NATIVE_WRITE)
214 m_filedes_locked
=false;
215 m_filedes
=open(filename
,O_WRONLY
|O_CREAT
,0644);
221 m_filedes_locked
= !flock(m_filedes
,LOCK_EX
|LOCK_NB
);
222 if (!m_filedes_locked
)
224 // this check might not be necessary, it might be sufficient to just fail and close if no exclusive lock possible
225 if (errno
== EWOULDBLOCK
)
227 // FAILED exclusive locking because someone else has a lock
231 else // failed for some other reason, try to keep a shared lock at least
233 m_filedes_locked
= !flock(m_filedes
,LOCK_SH
|LOCK_NB
);
240 if (!wantAppendTo
) ftruncate(m_filedes
,0);
244 if (!fstat64(m_filedes
,&st
)) SetPosition(st
.st_size
);
250 if (m_filedes
>= 0 && allow_async
>1) fcntl(m_filedes
,F_NOCACHE
,1);
253 if (minbufs
* bufsize
>= 16384) m_bufspace
.Resize((minbufs
*bufsize
+4095)&~4095);
255 m_fp
=fopen(filename
,wantAppendTo
? "a+b" : "wb");
256 if (wantAppendTo
&& m_fp
)
257 fseek(m_fp
,0,SEEK_END
);
263 #ifdef WDL_WIN32_NATIVE_WRITE
264 // todo, async close stuff?
265 if (m_fh
!= INVALID_HANDLE_VALUE
&& m_async
)
270 m_empties
.Empty(true);
271 m_pending
.Empty(true);
273 if (m_fh
!= INVALID_HANDLE_VALUE
) CloseHandle(m_fh
);
274 m_fh
=INVALID_HANDLE_VALUE
;
275 #elif defined(WDL_POSIX_NATIVE_WRITE)
278 if (m_bufspace
.GetSize() > 0 && m_bufspace_used
>0)
280 int v
=(int)pwrite(m_filedes
,m_bufspace
.Get(),m_bufspace_used
,m_file_position
);
281 if (v
>0) m_file_position
+=v
;
282 if (m_file_position
> m_file_max_position
) m_file_max_position
=m_file_position
;
285 if (m_filedes_locked
) flock(m_filedes
,LOCK_UN
);
291 if (m_fp
) fclose(m_fp
);
299 #ifdef WDL_WIN32_NATIVE_WRITE
300 return (m_fh
!= INVALID_HANDLE_VALUE
);
301 #elif defined(WDL_POSIX_NATIVE_WRITE)
302 return m_filedes
>= 0;
309 int Write(const void *buf
, int len
)
311 #ifdef WDL_WIN32_NATIVE_WRITE
312 if (m_fh
== INVALID_HANDLE_VALUE
) return 0;
319 if (!m_empties
.GetSize())
321 WDL_FileWrite__WriteEnt
*ent
=m_pending
.Get(0);
326 if (GetOverlappedResult(m_fh
,&ent
->m_ol
,&s
,FALSE
)||
327 (wasabort
=(GetLastError()==ERROR_OPERATION_ABORTED
)))
333 if (!RunAsyncWrite(ent
,false)) m_empties
.Add(ent
);
345 WDL_FileWrite__WriteEnt
*ent
=m_empties
.Get(0);
348 if (m_pending
.GetSize()>=m_async_maxbufs
)
353 if (!(ent
=m_empties
.Get(0)))
354 m_empties
.Add(ent
= new WDL_FileWrite__WriteEnt(m_async_bufsize
)); // new buffer
359 int ml
=ent
->m_bufsz
-ent
->m_bufused
;
361 memcpy(ent
->m_bufptr
+ent
->m_bufused
,(const char *)buf
+ rdpos
,ml
);
367 if (ent
->m_bufused
>= ent
->m_bufsz
)
369 if (RunAsyncWrite(ent
,true)) m_empties
.Delete(0); // if queued remove from list
377 WriteFile(m_fh
,buf
,len
,&dw
,NULL
);
379 if (m_file_position
>m_file_max_position
) m_file_max_position
=m_file_position
;
382 #elif defined(WDL_POSIX_NATIVE_WRITE)
383 if (m_bufspace
.GetSize()>0)
385 char *rdptr
= (char *)buf
;
389 int amt
= m_bufspace
.GetSize() - m_bufspace_used
;
392 if (amt
>rdlen
) amt
=rdlen
;
393 memcpy((char *)m_bufspace
.Get()+m_bufspace_used
,rdptr
,amt
);
394 m_bufspace_used
+= amt
;
398 if (m_file_position
+m_bufspace_used
> m_file_max_position
) m_file_max_position
=m_file_position
+ m_bufspace_used
;
400 if (m_bufspace_used
>= m_bufspace
.GetSize())
402 int v
=(int)pwrite(m_filedes
,m_bufspace
.Get(),m_bufspace_used
,m_file_position
);
403 if (v
>0) m_file_position
+=v
;
411 int v
=(int)pwrite(m_filedes
,buf
,len
,m_file_position
);
412 if (v
>0) m_file_position
+=v
;
413 if (m_file_position
> m_file_max_position
) m_file_max_position
=m_file_position
;
417 return fwrite(buf
,1,len
,m_fp
);
423 WDL_FILEWRITE_POSTYPE
GetSize()
425 #ifdef WDL_WIN32_NATIVE_WRITE
426 if (m_fh
== INVALID_HANDLE_VALUE
) return 0;
428 DWORD l
=GetFileSize(m_fh
,&h
);
429 WDL_FILEWRITE_POSTYPE tmp
=(((WDL_FILEWRITE_POSTYPE
)h
)<<32)|l
;
430 WDL_FILEWRITE_POSTYPE tmp2
=GetPosition();
431 if (tmp
<m_file_max_position
) return m_file_max_position
;
432 if (tmp
<tmp2
) return tmp2
;
435 #elif defined(WDL_POSIX_NATIVE_WRITE)
436 if (m_filedes
< 0) return -1;
437 return m_file_max_position
;
439 if (!m_fp
) return -1;
440 int opos
=ftell(m_fp
);
441 fseek(m_fp
,0,SEEK_END
);
443 fseek(m_fp
,opos
,SEEK_SET
);
449 WDL_FILEWRITE_POSTYPE
GetPosition()
451 #ifdef WDL_WIN32_NATIVE_WRITE
452 if (m_fh
== INVALID_HANDLE_VALUE
) return -1;
454 WDL_FILEWRITE_POSTYPE pos
=m_file_position
;
457 WDL_FileWrite__WriteEnt
*ent
=m_empties
.Get(0);
458 if (ent
) pos
+=ent
->m_bufused
;
461 #elif defined(WDL_POSIX_NATIVE_WRITE)
462 if (m_filedes
< 0) return -1;
463 return m_file_position
+ m_bufspace_used
;
465 if (!m_fp
) return -1;
471 #ifdef WDL_WIN32_NATIVE_WRITE
473 bool RunAsyncWrite(WDL_FileWrite__WriteEnt
*ent
, bool updatePosition
) // returns true if ent is added to pending
475 if (ent
&& ent
->m_bufused
>0)
479 ent
->m_last_writepos
= m_file_position
;
480 m_file_position
+= ent
->m_bufused
;
481 if (m_file_position
>m_file_max_position
) m_file_max_position
=m_file_position
;
484 #ifdef WIN32_ASYNC_NOBUF_WRITE
485 if (ent
->m_bufused
&4095)
487 int offs
=(ent
->m_bufused
&4095);
491 *(WDL_FILEWRITE_POSTYPE
*)&ent
->m_ol
.Offset
= ent
->m_last_writepos
+ ent
->m_bufused
- offs
;
492 ResetEvent(ent
->m_ol
.hEvent
);
495 if (!ReadFile(m_fh
,tmp
,4096,&dw
,&ent
->m_ol
))
497 if (GetLastError() == ERROR_IO_PENDING
)
498 WaitForSingleObject(ent
->m_ol
.hEvent
,INFINITE
);
500 memcpy(ent
->m_bufptr
+ent
->m_bufused
,tmp
+offs
,4096-offs
);
502 ent
->m_bufused
+= 4096-offs
;
507 *(WDL_FILEWRITE_POSTYPE
*)&ent
->m_ol
.Offset
= ent
->m_last_writepos
;
509 ResetEvent(ent
->m_ol
.hEvent
);
511 if (!WriteFile(m_fh
,ent
->m_bufptr
,ent
->m_bufused
,&d
,&ent
->m_ol
))
513 if (GetLastError()==ERROR_IO_PENDING
)
524 void SyncOutput(bool syncall
)
528 if (RunAsyncWrite(m_empties
.Get(0),true)) m_empties
.Delete(0);
532 WDL_FileWrite__WriteEnt
*ent
=m_pending
.Get(0);
536 if (!GetOverlappedResult(m_fh
,&ent
->m_ol
,&s
,TRUE
) && GetLastError()==ERROR_OPERATION_ABORTED
)
539 if (!RunAsyncWrite(ent
,false)) m_empties
.Add(ent
);
553 bool SetPosition(WDL_FILEWRITE_POSTYPE pos
) // returns 0 on success
555 #ifdef WDL_WIN32_NATIVE_WRITE
556 if (m_fh
== INVALID_HANDLE_VALUE
) return true;
561 if (m_file_position
>m_file_max_position
) m_file_max_position
=m_file_position
;
563 #ifdef WIN32_ASYNC_NOBUF_WRITE
564 if (m_file_position
&4095)
566 WDL_FileWrite__WriteEnt
*ent
=m_empties
.Get(0);
569 int psz
=(int) (m_file_position
&4095);
571 m_file_position
-= psz
;
572 *(WDL_FILEWRITE_POSTYPE
*)&ent
->m_ol
.Offset
= m_file_position
;
573 ResetEvent(ent
->m_ol
.hEvent
);
576 if (!ReadFile(m_fh
,ent
->m_bufptr
,4096,&dwo
,&ent
->m_ol
))
578 if (GetLastError() == ERROR_IO_PENDING
)
579 WaitForSingleObject(ent
->m_ol
.hEvent
,INFINITE
);
581 ent
->m_bufused
=(int)psz
;
589 if (m_file_position
>m_file_max_position
) m_file_max_position
=m_file_position
;
591 LONG high
=(LONG
) (m_file_position
>>32);
592 return SetFilePointer(m_fh
,(LONG
)(m_file_position
&((WDL_FILEWRITE_POSTYPE
)0xFFFFFFFF)),&high
,FILE_BEGIN
)==0xFFFFFFFF && GetLastError() != NO_ERROR
;
593 #elif defined(WDL_POSIX_NATIVE_WRITE)
595 if (m_filedes
< 0) return true;
596 if (m_bufspace
.GetSize() > 0 && m_bufspace_used
>0)
598 int v
=(int)pwrite(m_filedes
,m_bufspace
.Get(),m_bufspace_used
,m_file_position
);
599 if (v
>0) m_file_position
+=v
;
600 if (m_file_position
> m_file_max_position
) m_file_max_position
=m_file_position
;
604 m_file_position
= pos
; // seek!
605 if (m_file_position
>m_file_max_position
) m_file_max_position
=m_file_position
;
608 if (!m_fp
) return true;
609 return !!fseek(m_fp
,pos
,SEEK_SET
);
613 WDL_FILEWRITE_POSTYPE m_file_position
, m_file_max_position
;
615 #ifdef WDL_WIN32_NATIVE_WRITE
616 HANDLE
GetHandle() { return m_fh
; }
620 int m_async_bufsize
, m_async_minbufs
, m_async_maxbufs
;
622 WDL_PtrList
<WDL_FileWrite__WriteEnt
> m_empties
;
623 WDL_PtrList
<WDL_FileWrite__WriteEnt
> m_pending
;
625 #elif defined(WDL_POSIX_NATIVE_WRITE)
626 int GetHandle() { return m_filedes
; }
628 WDL_HeapBuf m_bufspace
;
632 bool m_filedes_locked
;
635 int GetHandle() { return fileno(m_fp
); }