context FEATURE public API for parsing schemas
diff --git a/src/common.c b/src/common.c
index 912ee13..ab3b71f 100644
--- a/src/common.c
+++ b/src/common.c
@@ -12,12 +12,18 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 #define _XOPEN_SOURCE
+#define _DEFAULT_SOURCE
 
+#include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
-#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "common.h"
 #include "tree_schema.h"
@@ -242,43 +248,59 @@
 }
 
 LY_ERR
-lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt)
+ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
 {
-    int i;
-    struct tm tm, tm_;
-    char *r;
+    struct stat sb;
+    long pagesize;
+    size_t m;
 
-    LY_CHECK_ARG_RET(ctx, date, LY_EINVAL);
-    LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx, date_len), LY_EINVAL);
+    assert(length);
+    assert(addr);
+    assert(fd >= 0);
 
-    /* check format */
-    for (i = 0; i < date_len; i++) {
-        if (i == 4 || i == 7) {
-            if (date[i] != '-') {
-                goto error;
-            }
-        } else if (!isdigit(date[i])) {
-            goto error;
-        }
+    if (fstat(fd, &sb) == -1) {
+        LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
+        return LY_ESYS;
     }
-
-    /* check content, e.g. 2018-02-31 */
-    memset(&tm, 0, sizeof tm);
-    r = strptime(date, "%Y-%m-%d", &tm);
-    if (!r || r != &date[LY_REV_SIZE - 1]) {
-        goto error;
+    if (!S_ISREG(sb.st_mode)) {
+        LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
+        return LY_ESYS;
     }
-    memcpy(&tm_, &tm, sizeof tm);
-    mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
-    if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */
-        /* checking days is enough, since other errors
-         * have been checked by strptime() */
-        goto error;
+    if (!sb.st_size) {
+        *addr = NULL;
+        return LY_SUCCESS;
+    }
+    pagesize = sysconf(_SC_PAGESIZE);
+
+    m = sb.st_size % pagesize;
+    if (m && pagesize - m >= 1) {
+        /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
+        *length = sb.st_size + 1;
+        *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
+    } else {
+        /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
+         * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
+         * Therefore we have to do the following hack with double mapping. First, the required number of bytes
+         * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
+         * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
+         * where the anonymous mapping starts. */
+        *length = sb.st_size + pagesize;
+        *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
+    }
+    if (*addr == MAP_FAILED) {
+        LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
+        return LY_ESYS;
     }
 
     return LY_SUCCESS;
+}
 
-error:
-    LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
-    return LY_EINVAL;
+LY_ERR
+ly_munmap(void *addr, size_t length)
+{
+    if (munmap(addr, length)) {
+        return LY_ESYS;
+    }
+    return LY_SUCCESS;
 }