FileInMemoryManager.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. /* FileInMemoryManager.cpp
  2. *
  3. * Copyright (C) 2017 David Weenink
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "FileInMemoryManager.h"
  19. #include "Collection.h"
  20. #include "oo_DESTROY.h"
  21. #include "FileInMemoryManager_def.h"
  22. #include "oo_COPY.h"
  23. #include "FileInMemoryManager_def.h"
  24. #include "oo_EQUAL.h"
  25. #include "FileInMemoryManager_def.h"
  26. #include "oo_CAN_WRITE_AS_ENCODING.h"
  27. #include "FileInMemoryManager_def.h"
  28. #include "oo_WRITE_TEXT.h"
  29. #include "FileInMemoryManager_def.h"
  30. #include "oo_READ_TEXT.h"
  31. #include "FileInMemoryManager_def.h"
  32. #include "oo_WRITE_BINARY.h"
  33. #include "FileInMemoryManager_def.h"
  34. #include "oo_READ_BINARY.h"
  35. #include "FileInMemoryManager_def.h"
  36. #include "oo_DESCRIPTION.h"
  37. #include "FileInMemoryManager_def.h"
  38. #include <errno.h>
  39. /*
  40. File open and read emulations. The FILE * is internally used as a pointer to the index of the file in the Set.
  41. List of open files has to contain per file: index, position, length (bytes), pointer to data
  42. */
  43. Thing_implement (FileInMemoryManager, Daata, 0);
  44. void structFileInMemoryManager :: v_info () {
  45. FileInMemoryManager_Parent :: v_info ();
  46. MelderInfo_writeLine (U"Number of files: ", files -> size);
  47. MelderInfo_writeLine (U"Total number of bytes: ", FileInMemorySet_getTotalNumberOfBytes (files.get()));
  48. }
  49. bool FileInMemoryManager_hasDirectory (FileInMemoryManager me, conststring32 name) {
  50. return FileInMemorySet_hasDirectory (my files.get(), name);
  51. }
  52. autoFileInMemoryManager FileInMemoryManager_create (FileInMemorySet files) {
  53. try {
  54. autoFileInMemoryManager me = Thing_new (FileInMemoryManager);
  55. my files = Data_copy (files);
  56. my openFiles = FileInMemorySet_create ();
  57. my openFiles -> _initializeOwnership (false);
  58. return me;
  59. } catch (MelderError) {
  60. Melder_throw (U"");
  61. }
  62. }
  63. /*
  64. integer SortedSetOfLong_Lookup (SortedSetOfLong me, integer number) {
  65. if (my size == 0) return 0; // empty set
  66. integer where = number - my at [my size] -> number; // compare with last item
  67. if (where > 0) return 0; // not at end
  68. if (where == 0) return my size;
  69. where = number - my at [1] -> number; // compare with first item
  70. if (where < 0) return 0; // not at start
  71. if (where == 0) return 1;
  72. integer left = 1, right = my size;
  73. while (left < right - 1) {
  74. integer mid = (left + right) / 2;
  75. where = number - my at [mid] -> number;
  76. if (where == 0) { // found
  77. return mid;
  78. } else if (where > 0) {
  79. left = mid;
  80. } else {
  81. right = mid;
  82. }
  83. }
  84. Melder_assert (right == left + 1);
  85. if ((number - my at [left] -> number) == 0) {
  86. return left;
  87. } else if ((number - my at [right] -> number) == 0) {
  88. return right;
  89. } else {
  90. return 0;
  91. }
  92. }
  93. */
  94. autoTable FileInMemoryManager_downto_Table (FileInMemoryManager me, bool openFilesOnly) {
  95. try {
  96. integer numberOfRows = openFilesOnly ? my openFiles -> size : my files -> size;
  97. autoTable thee = Table_createWithColumnNames (numberOfRows, U"path id size position");
  98. for (integer irow = 1; irow <= numberOfRows; irow ++) {
  99. FileInMemory fim = static_cast <FileInMemory> (openFilesOnly ? my openFiles -> at [irow] : my files -> at [irow]);
  100. Table_setStringValue (thee.get(), irow, 1, fim -> d_path.get());
  101. Table_setStringValue (thee.get(), irow, 2, fim -> d_id.get());
  102. Table_setNumericValue (thee.get(), irow, 3, fim -> d_numberOfBytes);
  103. Table_setNumericValue (thee.get(), irow, 4, fim -> d_position);
  104. }
  105. return thee;
  106. } catch (MelderError) {
  107. Melder_throw (me, U": no Table created.");
  108. }
  109. }
  110. autoFileInMemory FileInMemoryManager_createFile (FileInMemoryManager me, MelderFile file) {
  111. try {
  112. autoFileInMemory thee = FileInMemory_create (file);
  113. return thee;
  114. } catch (MelderError) {
  115. Melder_throw (me, U"Cannot create a FileInMemory object.");
  116. }
  117. }
  118. autoFileInMemorySet FileInMemoryManager_extractFiles (FileInMemoryManager me, kMelder_string which, conststring32 criterion) {
  119. return FileInMemorySet_extractFiles (my files.get(), which, criterion);
  120. }
  121. static integer _FileInMemoryManager_getIndexInOpenFiles (FileInMemoryManager me, FILE *stream) {
  122. integer filesIndex = reinterpret_cast<integer> (stream);
  123. Melder_require (filesIndex > 0 && filesIndex <= my files -> size, U": Invalid file index: ", filesIndex);
  124. FileInMemory fim = static_cast<FileInMemory> (my files -> at [filesIndex]);
  125. integer openFilesIndex = FileInMemorySet_lookUp (my openFiles.get(), fim -> d_path.get());
  126. return openFilesIndex;
  127. }
  128. /*
  129. From http://www.cplusplus.com/reference/cstdio
  130. FILE * fopen ( const char * filename, const char * mode );
  131. Open file
  132. Opens the file whose name is specified in the parameter filename and associates it with a stream that can be identified in future operations by the FILE pointer returned.
  133. The operations that are allowed on the stream and how these are performed are defined by the mode parameter.
  134. The returned stream is fully buffered by default if it is known to not refer to an interactive device (see setbuf).
  135. The returned pointer can be disassociated from the file by calling fclose or freopen. All opened files are automatically closed on normal program termination.
  136. The running environment supports at least FOPEN_MAX files open simultaneously.
  137. Parameters
  138. filename
  139. C string containing the name of the file to be opened.
  140. Its value shall follow the file name specifications of the running environment and can include a path (if supported by the system).
  141. mode
  142. C string containing a file access mode. It can be:
  143. "r" read: Open file for input operations. The file must exist.
  144. "w" write: Create an empty file for output operations. If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.
  145. "a" append: Open file for output at the end of a file. Output operations always write data at the end of the file, expanding it. Repositioning operations (fseek, fsetpos, rewind) are ignored. The file is created if it does not exist.
  146. "r+" read/update: Open a file for update (both for input and output). The file must exist.
  147. "w+" write/update: Create an empty file and open it for update (both for input and output). If a file with the same name already exists its contents are discarded and the file is treated as a new empty file.
  148. "a+" append/update: Open a file for update (both for input and output) with all output operations writing data at the end of the file. Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file. The file is created if it does not exist.
  149. With the mode specifiers above the file is open as a text file. In order to open a file as a binary file, a "b" character has to be included in the mode string. This additional "b" character can either be appended at the end of the string (thus making the following compound modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+").
  150. The new C standard (C2011, which is not part of C++) adds a new standard subspecifier ("x"), that can be appended to any "w" specifier (to form "wx", "wbx", "w+x" or "w+bx"/"wb+x"). This subspecifier forces the function to fail if the file exists, instead of overwriting it.
  151. If additional characters follow the sequence, the behavior depends on the library implementation: some implementations may ignore additional characters so that for example an additional "t" (sometimes used to explicitly state a text file) is accepted.
  152. On some library implementations, opening or creating a text file with update mode may treat the stream instead as a binary file.
  153. Text files are files containing sequences of lines of text. Depending on the environment where the application runs, some special character conversion may occur in input/output operations in text mode to adapt them to a system-specific text file format. Although on some environments no conversions occur and both text files and binary files are treated the same way, using the appropriate mode improves portability.
  154. For files open for update (those which include a "+" sign), on which both input and output operations are allowed, the stream shall be flushed (fflush) or repositioned (fseek, fsetpos, rewind) before a reading operation that follows a writing operation. The stream shall be repositioned (fseek, fsetpos, rewind) before a writing operation that follows a reading operation (whenever that operation did not reach the end-of-file).
  155. Return Value
  156. If the file is successfully opened, the function returns a pointer to a FILE object that can be used to identify the stream on future operations.
  157. Otherwise, a null pointer is returned.
  158. On most library implementations, the errno variable is also set to a system-specific error code on failure.
  159. */
  160. FILE *FileInMemoryManager_fopen (FileInMemoryManager me, const char *filename, const char *mode) {
  161. try {
  162. integer index = 0;
  163. if (*mode == 'r') { // also covers mode == 'rb'
  164. index = FileInMemorySet_lookUp (my files.get(), Melder_peek8to32(filename));
  165. if (index > 0) {
  166. FileInMemory fim = (FileInMemory) my files -> at [index];
  167. if (fim -> d_position == 0) { // not open
  168. my openFiles -> addItem_ref (fim);
  169. } else { // reset position
  170. fim -> d_position = 0;
  171. }
  172. } else {
  173. // file does not exist, set error condition?
  174. }
  175. } else if (*mode == 'w') {
  176. }
  177. return reinterpret_cast<FILE *> (index);
  178. } catch (MelderError) {
  179. Melder_throw (U"File ", Melder_peek8to32(filename), U" cannot be opended.");
  180. }
  181. }
  182. /*
  183. From http://www.cplusplus.com/reference/cstdio 20171028
  184. void rewind ( FILE * stream );
  185. Set position of stream to the beginning
  186. Sets the position indicator associated with stream to the beginning of the file.
  187. The end-of-file and error internal indicators associated to the stream are cleared after a successful call to this function, and all effects from previous calls to ungetc on this stream are dropped.
  188. On streams open for update (read+write), a call to rewind allows to switch between reading and writing.
  189. Parameters
  190. stream
  191. Pointer to a FILE object that identifies the stream.
  192. Return Value
  193. none
  194. */
  195. void FileInMemoryManager_rewind (FileInMemoryManager me, FILE *stream) {
  196. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  197. if (openFilesIndex > 0) {
  198. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  199. fim -> d_position = 0; fim -> d_errno = 0; fim -> ungetChar = -1;
  200. }
  201. }
  202. /*
  203. From http://www.cplusplus.com/reference/cstdio 20171028
  204. int fclose ( FILE * stream );
  205. Close file
  206. Closes the file associated with the stream and disassociates it.
  207. All internal buffers associated with the stream are disassociated from it and flushed: the content of any unwritten output buffer is written and the content of any unread input buffer is discarded.
  208. Even if the call fails, the stream passed as parameter will no longer be associated with the file nor its buffers.
  209. Parameters
  210. stream
  211. Pointer to a FILE object that specifies the stream to be closed.
  212. Return Value
  213. If the stream is successfully closed, a zero value is returned.
  214. On failure, EOF is returned.
  215. */
  216. int FileInMemoryManager_fclose (FileInMemoryManager me, FILE *stream) {
  217. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  218. if (openFilesIndex > 0) {
  219. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  220. fim -> d_position = 0; fim -> d_errno = 0; fim -> ungetChar = -1;
  221. my openFiles -> removeItem (openFilesIndex);
  222. }
  223. return my errorNumber = 0; // always ok
  224. }
  225. /*
  226. From http://www.cplusplus.com/reference/cstdio 20171028
  227. int feof ( FILE * stream );
  228. Check end-of-file indicator
  229. Checks whether the end-of-File indicator associated with stream is set, returning a value different from zero if it is.
  230. This indicator is generally set by a previous operation on the stream that attempted to read at or past the end-of-file.
  231. Notice that stream's internal position indicator may point to the end-of-file for the next operation, but still, the end-of-file indicator may not be set until an operation attempts to read at that point.
  232. This indicator is cleared by a call to clearerr, rewind, fseek, fsetpos or freopen. Although if the position indicator is not repositioned by such a call, the next i/o operation is likely to set the indicator again.
  233. Parameters
  234. stream
  235. Pointer to a FILE object that identifies the stream.
  236. Return Value
  237. A non-zero value is returned in the case that the end-of-file indicator associated with the stream is set.
  238. Otherwise, zero is returned.
  239. */
  240. int FileInMemoryManager_feof (FileInMemoryManager me, FILE *stream) {
  241. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  242. int eof = 0;
  243. if (openFilesIndex > 0) {
  244. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  245. if (fim -> d_position >= fim -> d_numberOfBytes) {
  246. eof = 1;
  247. }
  248. }
  249. return eof;
  250. }
  251. /*
  252. From http://www.cplusplus.com/reference/cstdio 20171028
  253. int fseek ( FILE * stream, long int offset, int origin );
  254. Reposition stream position indicator
  255. Sets the position indicator associated with the stream to a new position.
  256. For streams open in binary mode, the new position is defined by adding offset to a reference position specified by origin.
  257. For streams open in text mode, offset shall either be zero or a value returned by a previous call to ftell, and origin shall necessarily be SEEK_SET.
  258. If the function is called with other values for these arguments, support depends on the particular system and library implementation (non-portable).
  259. The end-of-file internal indicator of the stream is cleared after a successful call to this function, and all effects from previous calls to ungetc on this stream are dropped.
  260. On streams open for update (read+write), a call to fseek allows to switch between reading and writing.
  261. Parameters
  262. stream
  263. Pointer to a FILE object that identifies the stream.
  264. offset
  265. Binary files: Number of bytes to offset from origin.
  266. Text files: Either zero, or a value returned by ftell.
  267. origin
  268. Position used as reference for the offset. It is specified by one of the following constants defined in <cstdio> exclusively to be used as arguments for this function:
  269. Constant Reference position
  270. SEEK_SET Beginning of file
  271. SEEK_CUR Current position of the file pointer
  272. SEEK_END End of file *
  273. * Library implementations are allowed to not meaningfully support SEEK_END (therefore, code using it has no real standard portability).
  274. Return Value
  275. If successful, the function returns zero.
  276. Otherwise, it returns non-zero value.
  277. If a read or write error occurs, the error indicator (ferror) is set.
  278. */
  279. int FileInMemoryManager_fseek (FileInMemoryManager me, FILE *stream, integer offset, int origin) {
  280. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  281. int errval = EBADF;
  282. if (openFilesIndex > 0) {
  283. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  284. integer newPosition = 0;
  285. if (origin == SEEK_SET) {
  286. newPosition = offset;
  287. } else if (origin == SEEK_CUR) {
  288. newPosition = fim -> d_position + offset;
  289. } else if (origin == SEEK_END) {
  290. newPosition = fim -> d_numberOfBytes + offset;
  291. } else {
  292. return my errorNumber = EINVAL;
  293. }
  294. if (newPosition < 0) { // > numberOfBytes is allowed
  295. newPosition = 0;
  296. }
  297. fim -> d_position = newPosition;
  298. fim -> ungetChar = -1;
  299. errval = 0;
  300. }
  301. return my errorNumber = errval;
  302. }
  303. /*
  304. From http://www.cplusplus.com/reference/cstdio 20171028
  305. long int ftell ( FILE * stream );
  306. Get current position in stream
  307. Returns the current value of the position indicator of the stream.
  308. For binary streams, this is the number of bytes from the beginning of the file.
  309. For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek (if there are characters put back using ungetc still pending of being read, the behavior is undefined).
  310. Parameters
  311. stream
  312. Pointer to a FILE object that identifies the stream.
  313. Return Value
  314. On success, the current value of the position indicator is returned.
  315. On failure, -1L is returned, and errno is set to a system-specific positive value.
  316. */
  317. integer FileInMemoryManager_ftell (FileInMemoryManager me, FILE *stream) {
  318. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  319. /* int errval = EBADF; */
  320. integer currentPosition = -1L;
  321. if (openFilesIndex > 0) {
  322. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  323. currentPosition = fim -> d_position;
  324. }
  325. return currentPosition;
  326. }
  327. /*
  328. From http://www.cplusplus.com/reference/cstdio 20171028
  329. char * fgets ( char * str, int num, FILE * stream );
  330. Get string from stream
  331. Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
  332. A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
  333. A terminating null character is automatically appended after the characters copied to str.
  334. Notice that fgets is quite different from gets: not only fgets accepts a stream argument, but also allows to specify the maximum size of str and includes in the string any ending newline character.
  335. Parameters
  336. str
  337. Pointer to an array of chars where the string read is copied.
  338. num
  339. Maximum number of characters to be copied into str (including the terminating null-character).
  340. stream
  341. Pointer to a FILE object that identifies an input stream.
  342. stdin can be used as argument to read from the standard input.
  343. Return Value
  344. On success, the function returns str.
  345. If the end-of-file is encountered while attempting to read a character, the eof indicator is set (feof). If this happens before any characters could be read, the pointer returned is a null pointer (and the contents of str remain unchanged).
  346. If a read error occurs, the error indicator (ferror) is set and a null pointer is also returned (but the contents pointed by str may have changed).
  347. */
  348. char *FileInMemoryManager_fgets (FileInMemoryManager me, char *str, int num, FILE *stream) {
  349. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  350. char *result = nullptr;
  351. Melder_require (openFilesIndex > 0, U": File should be open.");
  352. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  353. integer startPos = fim -> d_position;
  354. if (startPos < fim -> d_numberOfBytes) {
  355. integer i = 0, endPos = startPos + num;
  356. endPos = endPos < fim -> d_numberOfBytes ? endPos : fim -> d_numberOfBytes;
  357. const unsigned char * p = fim -> d_data + startPos;
  358. char *p_str = str;
  359. if (fim -> ungetChar > 0) {
  360. /*
  361. copy the ungetChar and advance one position in stream
  362. */
  363. *p_str ++ = fim -> ungetChar;
  364. p ++; i ++;
  365. fim -> ungetChar = -1;
  366. }
  367. while (i ++ < num && (*p_str ++ = *p) && *p ++ != '\n');
  368. str [i] = '\0';
  369. fim -> d_position += i;
  370. result = str; // everything ok, return the str pointer
  371. } else {
  372. fim -> d_errno = EOF;
  373. }
  374. return result;
  375. }
  376. /*
  377. From http://www.cplusplus.com/reference/cstdio 20171028
  378. int fgetc ( FILE * stream );
  379. Get character from stream
  380. Returns the character currently pointed by the internal file position indicator of the specified stream. The internal file position indicator is then advanced to the next character.
  381. If the stream is at the end-of-file when called, the function returns EOF and sets the end-of-file indicator for the stream (feof).
  382. If a read error occurs, the function returns EOF and sets the error indicator for the stream (ferror).
  383. fgetc and getc are equivalent, except that getc may be implemented as a macro in some libraries.
  384. Parameters
  385. stream
  386. Pointer to a FILE object that identifies an input stream.
  387. Return Value
  388. On success, the character read is returned (promoted to an int value).
  389. The return type is int to accommodate for the special value EOF, which indicates failure:
  390. If the position indicator was at the end-of-file, the function returns EOF and sets the eof indicator (feof) of stream.
  391. If some other reading error happens, the function also returns EOF, but sets its error indicator (ferror) instead.
  392. */
  393. int FileInMemoryManager_fgetc (FileInMemoryManager me, FILE *stream) {
  394. char str[4];
  395. (void) FileInMemoryManager_fgets (me, str, 1, stream);
  396. return FileInMemoryManager_feof (me, stream) ? EOF : static_cast<int> (*str);
  397. }
  398. /*
  399. From http://www.cplusplus.com/reference/cstdio 20171028
  400. size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
  401. Read block of data from stream
  402. Reads an array of count elements, each one with a size of size bytes, from the stream and stores them in the block of memory specified by ptr.
  403. The position indicator of the stream is advanced by the total amount of bytes read.
  404. The total amount of bytes read if successful is (size*count).
  405. Parameters
  406. ptr
  407. Pointer to a block of memory with a size of at least (size*count) bytes, converted to a void*.
  408. size
  409. Size, in bytes, of each element to be read.
  410. size_t is an unsigned integral type.
  411. count
  412. Number of elements, each one with a size of size bytes.
  413. size_t is an unsigned integral type.
  414. stream
  415. Pointer to a FILE object that specifies an input stream.
  416. Return Value
  417. The total number of elements successfully read is returned.
  418. If this number differs from the count parameter, either a reading error occurred or the end-of-file was reached while reading. In both cases, the proper indicator is set, which can be checked with ferror and feof, respectively.
  419. If either size or count is zero, the function returns zero and both the stream state and the content pointed by ptr remain unchanged.
  420. size_t is an unsigned integral type.
  421. */
  422. size_t FileInMemoryManager_fread (FileInMemoryManager me, void *ptr, size_t size, size_t count, FILE *stream) {
  423. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  424. Melder_require (openFilesIndex > 0 && size > 0 && count > 0, U": File should be open.");
  425. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  426. size_t result = 0;
  427. integer startPos = fim -> d_position;
  428. if (startPos < fim -> d_numberOfBytes) {
  429. integer i = 0, endPos = startPos + count * size;
  430. if (endPos > fim -> d_numberOfBytes) {
  431. count = (fim -> d_numberOfBytes - startPos) / size;
  432. endPos = startPos + count * size;
  433. fim -> d_errno = EOF;
  434. }
  435. integer numberOfBytes = count * size;
  436. const unsigned char * p = fim -> d_data + fim -> d_position;
  437. char * str = static_cast<char *> (ptr);
  438. while (i < numberOfBytes) {
  439. str [i ++] = *p ++;
  440. }
  441. fim -> d_position = endPos;
  442. }
  443. result = count;
  444. return result;
  445. }
  446. /*
  447. From http://www.cplusplus.com/reference/cstdio 20171028
  448. int ungetc ( int character, FILE * stream );
  449. Unget character from stream
  450. A character is virtually put back into an input stream, decreasing its internal file position as if a previous getc operation was undone.
  451. This character may or may not be the one read from the stream in the preceding input operation. In any case, the next character retrieved from stream is the character passed to this function, independently of the original one.
  452. Notice though, that this only affects further input operations on that stream, and not the content of the physical file associated with it, which is not modified by any calls to this function.
  453. Some library implementations may support this function to be called multiple times, making the characters available in the reverse order in which they were put back. Although this behavior has no standard portability guarantees, and further calls may simply fail after any number of calls beyond the first.
  454. If successful, the function clears the end-of-file indicator of stream (if it was currently set), and decrements its internal file position indicator if it operates in binary mode; In text mode, the position indicator has unspecified value until all characters put back with ungetc have been read or discarded.
  455. A call to fseek, fsetpos or rewind on stream will discard any characters previously put back into it with this function.
  456. If the argument passed for the character parameter is EOF, the operation fails and the input stream remains unchanged.
  457. Parameters
  458. character
  459. The int promotion of the character to be put back.
  460. The value is internally converted to an unsigned char when put back.
  461. stream
  462. Pointer to a FILE object that identifies an input stream.
  463. Return Value
  464. On success, the character put back is returned.
  465. If the operation fails, EOF is returned.
  466. */
  467. int FileInMemoryManager_ungetc (FileInMemoryManager me, int character, FILE * stream) {
  468. int result = EOF;
  469. if (character != EOF) {
  470. integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
  471. if (openFilesIndex > 0) {
  472. FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
  473. -- (fim -> d_position);
  474. result = fim -> ungetChar = character;
  475. }
  476. }
  477. return result;
  478. }
  479. /*
  480. From http://www.cplusplus.com/reference/cstdio 20171028
  481. int fprintf ( FILE * stream, const char * format, ... );
  482. Write formatted data to stream
  483. Writes the C string pointed by format to the stream. If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.
  484. After the format parameter, the function expects at least as many additional arguments as specified by format.
  485. Parameters
  486. stream
  487. Pointer to a FILE object that identifies an output stream.
  488. format
  489. C string that contains the text to be written to the stream.
  490. It can optionally contain embedded format specifiers that are replaced by the values specified in subsequent additional arguments and formatted as requested.
  491. A format specifier follows this prototype:
  492. %[flags][width][.precision][length]specifier
  493. Where the specifier character at the end is the most significant component, since it defines the type and the interpretation of its corresponding argument:
  494. specifier Output Example
  495. d or i Signed decimal integer 392
  496. u Unsigned decimal integer 7235
  497. o Unsigned octal 610
  498. x Unsigned hexadecimal integer 7fa
  499. X Unsigned hexadecimal integer (uppercase) 7FA
  500. f Decimal floating point, lowercase 392.65
  501. F Decimal floating point, uppercase 392.65
  502. e Scientific notation (mantissa/exponent), lowercase 3.9265e+2
  503. E Scientific notation (mantissa/exponent), uppercase 3.9265E+2
  504. g Use the shortest representation: %e or %f 392.65
  505. G Use the shortest representation: %E or %F 392.65
  506. a Hexadecimal floating point, lowercase -0xc.90fep-2
  507. A Hexadecimal floating point, uppercase -0XC.90FEP-2
  508. c Character a
  509. s String of characters sample
  510. p Pointer address b8000000
  511. n Nothing printed.
  512. The corresponding argument must be a pointer to a signed int.
  513. The number of characters written so far is stored in the pointed location.
  514. % A % followed by another % character will write a single % to the stream. %
  515. The format specifier can also contain sub-specifiers: flags, width, .precision and modifiers (in that order), which are optional and follow these specifications:
  516. flags description
  517. - Left-justify within the given field width; Right justification is the default (see width sub-specifier).
  518. + Forces to preceed the result with a plus or minus sign (+ or -) even for positive numbers. By default, only negative numbers are preceded with a - sign.
  519. (space) If no sign is going to be written, a blank space is inserted before the value.
  520. # Used with o, x or X specifiers the value is preceeded with 0, 0x or 0X respectively for values different than zero.
  521. Used with a, A, e, E, f, F, g or G it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written.
  522. 0 Left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier).
  523. width description
  524. (number) Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger.
  525. * The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
  526. .precision description
  527. .number For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
  528. For a, A, e, E, f and F specifiers: this is the number of digits to be printed after the decimal point (by default, this is 6).
  529. For g and G specifiers: This is the maximum number of significant digits to be printed.
  530. For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.
  531. If the period is specified without an explicit value for precision, 0 is assumed.
  532. .* The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
  533. The length sub-specifier modifies the length of the data type. This is a chart showing the types used to interpret the corresponding arguments with and without length specifier (if a different type is used, the proper type promotion or conversion is performed, if allowed):
  534. specifiers
  535. length d i u o x X f F e E g G a A c s p n
  536. (none) int unsigned int double int char* void* int*
  537. hh signed char unsigned char signed char*
  538. h short int unsigned short int short int*
  539. l long int unsigned long int wint_t wchar_t* long int*
  540. ll long long int unsigned long long int long long int*
  541. j intmax_t uintmax_t intmax_t*
  542. z size_t size_t size_t*
  543. t ptrdiff_t ptrdiff_t ptrdiff_t*
  544. L long double
  545. Note that the c specifier takes an int (or wint_t) as argument, but performs the proper conversion to a char value (or a wchar_t) before formatting it for output.
  546. Note: Yellow rows indicate specifiers and sub-specifiers introduced by C99. See <cinttypes> for the specifiers for extended types.
  547. ... (additional arguments)
  548. Depending on the format string, the function may expect a sequence of additional arguments, each containing a value to be used to replace a format specifier in the format string (or a pointer to a storage location, for n).
  549. There should be at least as many of these arguments as the number of values specified in the format specifiers. Additional arguments are ignored by the function.
  550. Return Value
  551. On success, the total number of characters written is returned.
  552. If a writing error occurs, the error indicator (ferror) is set and a negative number is returned.
  553. If a multibyte character encoding error occurs while writing wide characters, errno is set to EILSEQ and a negative number is returned.
  554. */
  555. int FileInMemoryManager_fprintf (FileInMemoryManager me, FILE * stream, const char *format, ... ) {
  556. (void) me;
  557. va_list args;
  558. size_t bufferSize = -1;
  559. if (stream == stderr) {
  560. va_start (args, format);
  561. bufferSize = 3;
  562. autoNUMvector<char> buf ((integer) 0, bufferSize);
  563. int sizeNeeded = vsnprintf (buf.peek(), bufferSize, format, args); // find the size of the needed buffer
  564. va_end (args);
  565. if (sizeNeeded > bufferSize) {
  566. buf.reset ((integer) 0, sizeNeeded);
  567. va_start (args, format);
  568. (void) vsnprintf (buf.peek(), sizeNeeded, format, args);
  569. va_end (args);
  570. }
  571. bufferSize = sizeNeeded;
  572. // append the buffer
  573. //MelderInfo_writeLine (Melder_peek8to32 (buf.peek()));
  574. } else {
  575. //integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream); //
  576. }
  577. return bufferSize;
  578. }
  579. void test_FileInMemoryManager_io (void) {
  580. conststring32 path1 = U"~/kanweg1.txt";
  581. conststring32 path2 = U"~/kanweg2.txt";
  582. conststring32 lines1 [3] = { U"abcd\n", U"ef\n", U"ghijk\n" };
  583. conststring32 lines2 [3] = { U"lmno\n", U"pqr\n", U"stuvwxyz\n" };
  584. /*
  585. Create a test FileInMemorySet with two (text) files in it.
  586. */
  587. MelderInfo_writeLine (U"test_FileInMemoryManager_io:");
  588. MelderInfo_writeLine (U"\tCreating two files: ", path1, U" and ", path2);
  589. structMelderFile s_file1 = {} , s_file2 = {};
  590. MelderFile file1 = & s_file1, file2 = & s_file2;
  591. Melder_relativePathToFile (path1, file1);
  592. Melder_relativePathToFile (path2, file2);
  593. autoFileInMemorySet fims = FileInMemorySet_create ();
  594. FILE *f = fopen (Melder_peek32to8 (file1 -> path), "w");
  595. for (integer j = 0; j <= 2; j ++) {
  596. fputs (Melder_peek32to8 (lines1 [j]), f);
  597. }
  598. fclose (f);
  599. f = fopen (Melder_peek32to8 (file2 -> path), "w");
  600. for (integer j = 0; j <= 2; j ++) {
  601. fputs (Melder_peek32to8 (lines2 [j]), f);
  602. }
  603. fclose (f);
  604. MelderInfo_writeLine (U"\tCreating FileInMemorySet from two files...");
  605. autoFileInMemory fim1 = FileInMemory_create (file1);
  606. fims -> addItem_move (fim1.move());
  607. autoFileInMemory fim2 = FileInMemory_create (file2);
  608. fims -> addItem_move (fim2.move());
  609. /*
  610. Create the FileInMemoryManager and test
  611. */
  612. autoFileInMemoryManager me = FileInMemoryManager_create (fims.get());
  613. // fopen test
  614. MelderInfo_writeLine (U"\tOpen file ", file1 -> path);
  615. FILE * f1 = FileInMemoryManager_fopen (me.get(), Melder_peek32to8 (file1 -> path), "r");
  616. integer openFilesIndex1 = _FileInMemoryManager_getIndexInOpenFiles (me.get(), f1);
  617. Melder_assert (openFilesIndex1 == 1);
  618. MelderInfo_writeLine (U"\t\t ...opened");
  619. MelderInfo_writeLine (U"\tOpen file ", file2 -> path);
  620. FILE * f2 = FileInMemoryManager_fopen (me.get(), Melder_peek32to8 (file2 -> path), "r");
  621. integer openFilesIndex2 = _FileInMemoryManager_getIndexInOpenFiles (me.get(), f2);
  622. Melder_assert (openFilesIndex2 == 2);
  623. MelderInfo_writeLine (U"\t\t ...opened");
  624. FileInMemoryManager_fclose (me.get(), f2);
  625. Melder_assert (my openFiles -> size == 1);
  626. MelderInfo_writeLine (U"\tClosed file ", file2 -> path);
  627. // read from open text file
  628. MelderInfo_writeLine (U"\tRead as text file in memory: ", file1 -> path);
  629. char buf0 [200], buf1 [200];
  630. long nbuf = 200;
  631. FileInMemory fim = (FileInMemory) my files -> at [openFilesIndex1];
  632. FILE *file0 = fopen (Melder_peek32to8 (file1 -> path), "r");
  633. for (integer i = 0; i <= 2; i ++) {
  634. char *p0 = fgets (buf0, nbuf, file0);
  635. integer pos0 = ftell (file0);
  636. char *p1 = FileInMemoryManager_fgets (me.get(), buf1, nbuf, f1);
  637. integer pos1 = FileInMemoryManager_ftell (me.get(), f1);
  638. Melder_assert (Melder_equ (Melder_peek8to32 (buf0), Melder_peek8to32 (buf1)));
  639. Melder_assert (pos0 == pos1);
  640. Melder_assert (p0 == buf0 && p1 == buf1);
  641. MelderInfo_writeLine (U"\t\tRead 1 line. Positions: ", pos0, U" and ", pos1);
  642. }
  643. MelderInfo_writeLine (U"\t\tRead while at EOF, returns nullptr");
  644. char *shouldbenull = FileInMemoryManager_fgets (me.get(), buf1, nbuf, f1);
  645. Melder_assert (shouldbenull == nullptr);
  646. MelderInfo_writeLine (U"\tFinished reading... rewind ");
  647. // read as binary file
  648. rewind (file0);
  649. FileInMemoryManager_rewind (me.get(), f1);
  650. MelderInfo_writeLine (U"\tRead as binary file in memory: ", file1 -> path);
  651. Melder_assert (fim -> d_position == 0);
  652. integer count = 8;
  653. size_t nread0 = fread (buf0, 1, count, file0);
  654. size_t nread1 = FileInMemoryManager_fread (me.get(), buf1, 1, count, f1);
  655. MelderInfo_writeLine (U"\t\tRead ", nread0, U" and ", nread1, U" bytes");
  656. Melder_assert (nread0 == nread0);
  657. Melder_assert (fim -> d_position == count);
  658. nread0 = fread (buf0, 1, count, file0);
  659. nread1 = FileInMemoryManager_fread (me.get(), buf1, 1, count, f1);
  660. MelderInfo_writeLine (U"\t\tRead ", nread0, U" and ", nread1, U" bytes");
  661. Melder_assert (nread0 == nread1);
  662. int eof0 = feof (file0);
  663. int eof1 = FileInMemoryManager_feof (me.get(), f1);
  664. MelderInfo_writeLine (U"\tEOF ? ", eof0, U" and ", eof1);
  665. Melder_assert (eof0 != 0 && eof1 != 0);
  666. // clean up
  667. MelderFile_delete (file1);
  668. MelderFile_delete (file2);
  669. MelderInfo_writeLine (U"test_FileInMemoryManager_io: OK");
  670. }
  671. /* End of file FileInMemoryManager.cpp */