diff --git a/src/runtime/c/data.h b/src/runtime/c/data.h index 80e0742e9..db7394bd8 100644 --- a/src/runtime/c/data.h +++ b/src/runtime/c/data.h @@ -121,7 +121,8 @@ struct PGF_INTERNAL_DECL PgfPGFRoot { #pragma GCC diagnostic ignored "-Wattributes" struct PgfPGF : DB { - PGF_INTERNAL_DECL PgfPGF(const char* fpath) : DB(fpath) {}; + PGF_INTERNAL_DECL PgfPGF(const char* fpath, int flags, int mode) + : DB(fpath, flags, mode) {}; PGF_INTERNAL_DECL ~PgfPGF() {}; }; diff --git a/src/runtime/c/db.cxx b/src/runtime/c/db.cxx index dd9ccd240..c32e432cd 100644 --- a/src/runtime/c/db.cxx +++ b/src/runtime/c/db.cxx @@ -270,26 +270,35 @@ struct malloc_state moffset root_offset; }; -DB::DB(const char* pathname) { - fd = open(pathname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - if (fd < 0) - throw std::system_error(errno, std::generic_category()); - - size_t file_size = lseek(fd, 0, SEEK_END); - - if (file_size == ((off_t) -1)) - throw std::system_error(errno, std::generic_category()); - +DB::DB(const char* pathname, int flags, int mode) { + size_t file_size; bool is_new = false; - if (file_size == 0) { + + if (pathname == NULL) { + fd = -1; file_size = getpagesize(); - if (ftruncate(fd, file_size) < 0) + is_new = true; + } else { + fd = open(pathname, flags, mode); + if (fd < 0) throw std::system_error(errno, std::generic_category()); - is_new = true; + + file_size = lseek(fd, 0, SEEK_END); + if (file_size == ((off_t) -1)) + throw std::system_error(errno, std::generic_category()); + + is_new = false; + if (file_size == 0) { + file_size = getpagesize(); + if (ftruncate(fd, file_size) < 0) + throw std::system_error(errno, std::generic_category()); + is_new = true; + } } + int mflags = (fd < 0) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED; ms = (malloc_state*) - mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + mmap(NULL, file_size, PROT_READ | PROT_WRITE, mflags, fd, 0); if (ms == MAP_FAILED) throw std::system_error(errno, std::generic_category()); @@ -306,7 +315,9 @@ DB::~DB() { ms->top + size + sizeof(size_t); munmap(ms,size); - close(fd); + + if (fd >= 0) + close(fd); } void DB::sync() @@ -803,8 +814,10 @@ DB::malloc_internal(size_t bytes) size_t new_size = old_size + alloc_size; - if (ftruncate(fd, new_size) < 0) - throw std::system_error(errno, std::generic_category()); + if (fd >= 0) { + if (ftruncate(fd, new_size) < 0) + throw std::system_error(errno, std::generic_category()); + } malloc_state* new_ms = (malloc_state*) mremap(ms, old_size, new_size, MREMAP_MAYMOVE); diff --git a/src/runtime/c/db.h b/src/runtime/c/db.h index 457949958..1e2139945 100644 --- a/src/runtime/c/db.h +++ b/src/runtime/c/db.h @@ -63,7 +63,7 @@ private: friend class PgfReader; public: - DB(const char* pathname); + DB(const char* pathname, int flags, int mode); ~DB(); template diff --git a/src/runtime/c/pgf.cxx b/src/runtime/c/pgf.cxx index a722b6c7f..a3aac5c32 100644 --- a/src/runtime/c/pgf.cxx +++ b/src/runtime/c/pgf.cxx @@ -1,3 +1,4 @@ +#include #include "data.h" #include "reader.h" @@ -10,21 +11,14 @@ pgf_exn_clear(PgfExn* err) } PGF_API -PgfPGF *pgf_read(const char* fpath, PgfExn* err) +PgfPGF *pgf_read_pgf(const char* fpath, PgfExn* err) { PgfPGF *pgf = NULL; pgf_exn_clear(err); try { - std::string fpath_n = fpath; - size_t len = fpath_n.length(); - if (len > 4 && fpath_n.substr(len-4) == ".pgf") - fpath_n[len-3] = 'n'; - else if (!(len > 4 && fpath_n.substr(len-4) == ".ngf")) - fpath_n += ".ngf"; - - pgf = new PgfPGF(fpath_n.c_str()); + pgf = new PgfPGF(NULL, 0, 0); if (DB::get_root() == 0) { std::ifstream in(fpath, std::ios::binary); @@ -55,6 +49,80 @@ PgfPGF *pgf_read(const char* fpath, PgfExn* err) return NULL; } +PGF_API +PgfPGF *pgf_boot_ngf(const char* pgf_path, const char* ngf_path, PgfExn* err) +{ + PgfPGF *pgf = NULL; + + pgf_exn_clear(err); + + try { + pgf = new PgfPGF(ngf_path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); + + std::ifstream in(pgf_path, std::ios::binary); + if (in.fail()) { + throw std::system_error(errno, std::generic_category()); + } + + PgfReader rdr(&in); + ref pgf_root = rdr.read_pgf(); + + pgf->set_root(pgf_root); + + DB::sync(); + + return pgf; + } catch (std::system_error& e) { + err->type = PGF_EXN_SYSTEM_ERROR; + err->code = e.code().value(); + } catch (pgf_error& e) { + err->type = PGF_EXN_PGF_ERROR; + err->msg = strdup(e.what()); + } + + if (pgf != NULL) + delete pgf; + + return NULL; +} + +PGF_API +PgfPGF *pgf_read_ngf(const char *fpath, PgfExn* err) +{ + PgfPGF *pgf = NULL; + + pgf_exn_clear(err); + + try { + pgf = new PgfPGF(fpath, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + if (DB::get_root() == 0) { + ref pgf = DB::malloc(); + pgf->major_version = 2; + pgf->minor_version = 0; + pgf->gflags = 0; + pgf->abstract.name = DB::malloc(); + pgf->abstract.name->size = 0; + pgf->abstract.aflags = 0; + pgf->abstract.funs = 0; + pgf->abstract.cats = 0; + } + + return pgf; + } catch (std::system_error& e) { + err->type = PGF_EXN_SYSTEM_ERROR; + err->code = e.code().value(); + } catch (pgf_error& e) { + err->type = PGF_EXN_PGF_ERROR; + err->msg = strdup(e.what()); + } + + if (pgf != NULL) + delete pgf; + + return NULL; +} + PGF_API void pgf_free(PgfPGF *pgf) { diff --git a/src/runtime/c/pgf.h b/src/runtime/c/pgf.h index 21eccabb3..7e62fb28b 100644 --- a/src/runtime/c/pgf.h +++ b/src/runtime/c/pgf.h @@ -80,9 +80,25 @@ typedef struct { const char *msg; } PgfExn; +/* Reads a PGF file and keeps it in memory. */ PGF_API_DECL -PgfPGF *pgf_read(const char* fpath, PgfExn* err); +PgfPGF *pgf_read_pgf(const char* fpath, PgfExn* err); +/* Reads a PGF file and stores the unpacked data in an NGF file + * ready to be shared with other process, or used for quick startup. + * The NGF file is platform dependent and should not be copied + * between machines. */ +PGF_API_DECL +PgfPGF *pgf_boot_ngf(const char* pgf_path, const char* ngf_path, PgfExn* err); + +/* Tries to read the grammar from an already booted NGF file. + * If the file does not exist then a new one is created, and the + * grammar is set to be empty. It can later be populated with + * rules dynamically. */ +PGF_API_DECL +PgfPGF *pgf_read_ngf(const char* fpath, PgfExn* err); + +/* Release the grammar when it is no longer needed. */ PGF_API_DECL void pgf_free(PgfPGF *pgf); diff --git a/src/runtime/haskell/PGF2.hsc b/src/runtime/haskell/PGF2.hsc index 0f434533e..c1c265df9 100644 --- a/src/runtime/haskell/PGF2.hsc +++ b/src/runtime/haskell/PGF2.hsc @@ -14,7 +14,7 @@ ------------------------------------------------- module PGF2 (-- * PGF - PGF,readPGF, + PGF,readPGF,bootNGF,readNGF, -- * Abstract syntax AbsName,abstractName, @@ -48,7 +48,44 @@ readPGF fpath = withCString fpath $ \c_fpath -> allocaBytes (#size PgfExn) $ \c_exn -> mask_ $ do - c_pgf <- pgf_read c_fpath c_exn + c_pgf <- pgf_read_pgf c_fpath c_exn + ex_type <- (#peek PgfExn, type) c_exn :: IO (#type PgfExnType) + if ex_type == (#const PGF_EXN_NONE) + then do fptr <- newForeignPtr pgf_free_fptr c_pgf + return (PGF fptr Map.empty) + else if ex_type == (#const PGF_EXN_SYSTEM_ERROR) + then do errno <- (#peek PgfExn, code) c_exn + ioError (errnoToIOError "readPGF" (Errno errno) Nothing (Just fpath)) + else do c_msg <- (#peek PgfExn, msg) c_exn + msg <- peekCString c_msg + free c_msg + throwIO (PGFError msg) + +bootNGF :: FilePath -> FilePath -> IO PGF +bootNGF pgf_path ngf_path = + withCString pgf_path $ \c_pgf_path -> + withCString ngf_path $ \c_ngf_path -> + allocaBytes (#size PgfExn) $ \c_exn -> + mask_ $ do + c_pgf <- pgf_boot_ngf c_pgf_path c_ngf_path c_exn + ex_type <- (#peek PgfExn, type) c_exn :: IO (#type PgfExnType) + if ex_type == (#const PGF_EXN_NONE) + then do fptr <- newForeignPtr pgf_free_fptr c_pgf + return (PGF fptr Map.empty) + else if ex_type == (#const PGF_EXN_SYSTEM_ERROR) + then do errno <- (#peek PgfExn, code) c_exn + ioError (errnoToIOError "bootNGF" (Errno errno) Nothing (Just pgf_path)) + else do c_msg <- (#peek PgfExn, msg) c_exn + msg <- peekCString c_msg + free c_msg + throwIO (PGFError msg) + +readNGF :: FilePath -> IO PGF +readNGF fpath = + withCString fpath $ \c_fpath -> + allocaBytes (#size PgfExn) $ \c_exn -> + mask_ $ do + c_pgf <- pgf_read_ngf c_fpath c_exn ex_type <- (#peek PgfExn, type) c_exn :: IO (#type PgfExnType) if ex_type == (#const PGF_EXN_NONE) then do fptr <- newForeignPtr pgf_free_fptr c_pgf diff --git a/src/runtime/haskell/PGF2/FFI.hsc b/src/runtime/haskell/PGF2/FFI.hsc index 22583f655..47f7b269c 100644 --- a/src/runtime/haskell/PGF2/FFI.hsc +++ b/src/runtime/haskell/PGF2/FFI.hsc @@ -31,8 +31,14 @@ foreign import ccall unsafe "pgf_utf8_decode" foreign import ccall unsafe "pgf_utf8_encode" pgf_utf8_encode :: Word32 -> Ptr CString -> IO () -foreign import ccall "pgf_read" - pgf_read :: CString -> Ptr PgfExn -> IO (Ptr PgfPGF) +foreign import ccall "pgf_read_pgf" + pgf_read_pgf :: CString -> Ptr PgfExn -> IO (Ptr PgfPGF) + +foreign import ccall "pgf_boot_ngf" + pgf_boot_ngf :: CString -> CString -> Ptr PgfExn -> IO (Ptr PgfPGF) + +foreign import ccall "pgf_read_ngf" + pgf_read_ngf :: CString -> Ptr PgfExn -> IO (Ptr PgfPGF) foreign import ccall "&pgf_free" pgf_free_fptr :: FinalizerPtr PgfPGF