AMemFile
(0)
- ファイルをファイルハンドル経由でアクセスせずに、全部メモリ上に読み込んで自由にアクセスしたらプログラムが簡単になるんじゃないかと考えました。ファイル内のテキストの任意の部分を char * で指し示すことができるようになります。
- ファイルサイズに合わせてメモリサイズを自動調整する機能を持っていて、それを利用して単に自動伸長型の配列として使うこともできます。
(1) ver.1.00 [2024.09.02] (133行)
- AMemFile : 必要な情報を集めた構造体。
- AMemFile *AMemFile_newSiz(int flags, int siz): 中身が空っぽのAMemFileを作成。
- flags: AMemFile_Err=エラー処理をライブラリ任せにはしない, AMemFile_Text=テキストモード
- siz: 初期バッファサイズ。0を指定してはいけない。普通は16以上を指定。
- AMemFile *AMemFile_new(int flags): siz=64KBでAMemFile_newSiz()する。
- void AMemFile_close(AMemFile *mp)
- int AMemFile_write(AMemFile *mp, const char *s, int n)
- int AMemFile_toReadMode(AMemFile *mp): もうwriteは使わないことを前提に末尾に16バイトの0x00を追加して、サイズを切り詰め、メモリを節約します。
- 追加された0x00は本体データであるp0~pには含まれません。多くの場合追加の0x00は1バイトで十分そうな気がしますが、まあ余裕を見て16バイトにしました。
- int AMemFile_load(AMemFile *mp, const char *path):下請け
- int AMemFile_save(AMemFile *mp, const char *path):下請け
- void AMemFile_eraseCr(AMemFile *mp):下請け
- AMemFile *AMemFile_open(int flags, const char *path)
- int AMemFile_saveClose(AMemFile *mp, const char *path)
- [補足説明]
- テキストモードを指定してAMemFile_open()すると、読み込み直後にAMemFile_eraseCr()します。またAMemFile_save時には"wt"でfopenします。それ以外の差異はありません。
- たとえテキストモードでもload時は"rt"で読み込んだりはしません。なぜならLinuxなどでは"rt"でfopenしても(Windowsから持ってきたファイルの)'\r'が消えないからです。だからOSにかかわらず"rb"で読み取って、AMemFile_eraseCr()で消します。
#include "acl1Tiny.c"
AClass(AMemFile) {
int bufSiz, flags;
char *p0, *p, *p1;
};
#define AMemFile_Err 1
#define AMemFile_Text 2
AStatic AMemFile *AMemFile_newSiz(int flags, int siz)
{
AMemFile *mp = malloc(sizeof (AMemFile));
if (mp == 0) {
outOfMemory:
if ((flags & AMemFile_Err) == 0) { aErrExit("AMemFile_new: out of memory"); }
return 0;
}
mp->p0 = malloc(siz);
if (mp->p0 == 0) { free(mp); goto outOfMemory; }
mp->bufSiz = siz;
mp->flags = flags;
mp->p = mp->p0;
mp->p1 = mp->p0 + siz;
return mp;
}
AStatic AMemFile *AMemFile_new(int flags) { return AMemFile_newSiz(flags, 64 * 1024); }
AStatic void AMemFile_close(AMemFile *mp)
{
if (mp != 0) {
free(mp->p0);
free(mp);
}
}
AStatic int AMemFile_write(AMemFile *mp, const char *s, int n)
{
retry:
if (n <= 0) { return 0; }
if (mp->p + n <= mp->p1) {
memcpy(mp->p, s, n);
mp->p += n;
return 0;
}
char *p = realloc(mp->p0, mp->bufSiz * 2);
if (p == 0) {
if ((mp->flags & AMemFile_Err) == 0) { aErrExit("AMemFile_write: out of memory"); }
return 1;
}
int i = mp->p - mp->p0;
mp->bufSiz *= 2;
mp->p0 = p;
mp->p = p + i;
mp->p1 = mp->p0 + mp->bufSiz;
goto retry;
}
AStatic int AMemFile_toReadMode(AMemFile *mp)
{
static char s[16];
int i = AMemFile_write(mp, s, 16);
if (i != 0) return i;
i = mp->p - mp->p0;
mp->p0 = realloc(mp->p0, i);
mp->p = mp->p0 + (i - 16);
mp->p1 = mp->p0 + i;
mp->bufSiz = i;
return 0;
}
AStatic int AMemFile_load(AMemFile *mp, const char *path)
// 1GBを超えるようなファイルサイズは想定していない.
{
int siz = 1024 * 1024, i;
char *t = malloc(siz);
FILE *fp= fopen(path, "rb");
if (fp == 0) {
if ((mp->flags & AMemFile_Err) == 0) { aErrExit("AMemFile_load: fopen error: %s", path); }
return 1;
}
do {
i = fread(t, 1, siz, fp);
AMemFile_write(mp, t, i);
} while (i >= siz); // 実際には i > siz になることはない.
fclose(fp);
free(t);
return 0;
}
AStatic int AMemFile_save(AMemFile *mp, const char *path)
{
const char *mod = "wb";
if ((mp->flags & AMemFile_Text) != 0) { mod = "wt"; }
FILE *fp = fopen(path, mod);
if (fp == 0) {
if ((mp->flags & AMemFile_Err) == 0) { aErrExit("AMemFile_save: fopen error: %s", path); }
return 1;
}
int i = fwrite(mp->p0, 1, mp->p - mp->p0, fp);
fclose(fp);
if (i < mp->p - mp->p0) {
if ((mp->flags & AMemFile_Err) == 0) { aErrExit("AMemFile_save: fwrite error: %s", path); }
return 1;
}
return 0;
}
AStatic void AMemFile_eraseCr(AMemFile *mp)
{
char *p = mp->p0, *q = p, *p1 = mp->p;
for (; p < p1; p++) {
if (*p != '\r') { *q++ = *p; }
}
mp->p = q;
}
AStatic AMemFile *AMemFile_open(int flags, const char *path)
{
AMemFile *mp = AMemFile_new(flags);
if (mp == 0) return 0;
if (AMemFile_load(mp, path) != 0) { AMemFile_close(mp); return 0; }
if ((flags & AMemFile_Text) != 0) { AMemFile_eraseCr(mp); }
AMemFile_toReadMode(mp);
return mp;
}
AStatic int AMemFile_saveClose(AMemFile *mp, const char *path)
{
int i = AMemFile_save(mp, path);
if (i != 0) { return i; }
AMemFile_close(mp);
return 0;
}