// recstore.h
/*
 *    Copyright (C) 2010 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#pragma once

#include "../util/file.h"

namespace mongo { 

using boost::uint32_t;
using boost::uint64_t;

/* Current version supports only consistent record sizes within a store. */

class BasicRecStore { 
    struct RecStoreHeader { 
        uint32_t version;
        uint32_t recsize;
        uint64_t leof; // logical eof, actual file might be prealloc'd further
        uint64_t firstDeleted; // 0 = no deleted recs
        uint32_t cleanShutdown; // 0 = clean
        char reserved[8192-8-8-4-4-4]; // we want our records page-aligned in the file if they are a multiple of a page's size -- so we make this 8KB with that goal
        RecStoreHeader() { 
            version = 65;
            recsize = 0;
            leof = sizeof(RecStoreHeader);
            firstDeleted = 0;
            cleanShutdown = 1;
            memset(reserved, 0, sizeof(reserved));
        }
    };

public:
    BasicRecStore(int _fileNumber) : fileNumber(_fileNumber) { }
    ~BasicRecStore();
    void init(const char *fn, unsigned recsize);
    fileofs insert(const char *buf, unsigned len);
    void update(fileofs o, const char *buf, unsigned len);
    void remove(fileofs o, unsigned len);
    void get(fileofs o, char *buf, unsigned len);

    int fileNumber; // this goes in DiskLoc::a

    string filename;

private:

    void writeHeader();
    File f;
    fileofs len;
    RecStoreHeader h; // h.reserved is wasteful here; fix later.
    void write(fileofs ofs, const char *data, unsigned len) { 
        f.write(ofs, data, len);
        massert( 10380 , "basicrecstore write io error", !f.bad());
    }
};

/* --- implementation --- */

inline BasicRecStore::~BasicRecStore() { 
    h.cleanShutdown = 0;
    if( f.is_open() ) {
        writeHeader();
        f.fsync();
    }
}

inline void BasicRecStore::writeHeader() { 
    write(0, (const char *) &h, 28); // update header in file for new leof
    uassert( 10115 , "file io error in BasicRecStore [1]", !f.bad()); 
}

inline fileofs BasicRecStore::insert(const char *buf, unsigned reclen) { 
    if( h.firstDeleted ) { 
        uasserted(11500, "deleted not yet implemented recstoreinsert");
    }
    massert( 10381 , "bad len", reclen == h.recsize);
    fileofs ofs = h.leof;
    h.leof += reclen;
    if( h.leof > len ) { 
        // grow the file.  we grow quite a bit to avoid excessive file system fragmentations
        len += (len / 8) + h.recsize;
        uassert( 10116 ,  "recstore file too big for 32 bit", len <= 0x7fffffff || sizeof(std::streamoff) > 4 );
        write(len, "", 0);
    }
    writeHeader();
    write(ofs, buf, reclen);
    uassert( 10117 , "file io error in BasicRecStore [2]", !f.bad());
    return ofs;
}

/* so far, it's ok to read or update a subset of a record */

inline void BasicRecStore::update(fileofs o, const char *buf, unsigned len) { 
    assert(o <= h.leof && o >= sizeof(RecStoreHeader));
    write(o, buf, len);
}

inline void BasicRecStore::get(fileofs o, char *buf, unsigned len) { 
    assert(o <= h.leof && o >= sizeof(RecStoreHeader));
    f.read(o, buf, len);
    massert( 10382 , "basicrestore::get I/O error", !f.bad());
}

inline void BasicRecStore::remove(fileofs o, unsigned len) { 
    uasserted(11501, "not yet implemented recstoreremove");
}

}
