1
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13//
14// Copyright 2005-2010 Google, Inc.
15// Author: sorenj@google.com (Jeffrey Sorensen)
16
17#include <fst/mapped-file.h>
18
19#include <errno.h>
20#include <fcntl.h>
21
22namespace fst {
23
24// Alignment required for mapping structures (in bytes.)  Regions of memory
25// that are not aligned upon a 128 bit boundary will be read from the file
26// instead.  This is consistent with the alignment boundary set in the
27// const and compact fst code.
28const int MappedFile::kArchAlignment = 16;
29
30MappedFile::MappedFile(const MemoryRegion &region) : region_(region) { }
31
32MappedFile::~MappedFile() {
33  if (region_.size != 0) {
34    if (region_.mmap != NULL) {
35      VLOG(1) << "munmap'ed " << region_.size << " bytes at " << region_.mmap;
36      if (munmap(region_.mmap, region_.size) != 0) {
37        LOG(ERROR) << "failed to unmap region: "<< strerror(errno);
38      }
39    } else {
40      operator delete(region_.data);
41    }
42  }
43}
44
45MappedFile* MappedFile::Allocate(size_t size) {
46  MemoryRegion region;
47  region.data = size == 0 ? NULL : operator new(size);
48  region.mmap = NULL;
49  region.size = size;
50  return new MappedFile(region);
51}
52
53MappedFile* MappedFile::Borrow(void *data) {
54  MemoryRegion region;
55  region.data = data;
56  region.mmap = data;
57  region.size = 0;
58  return new MappedFile(region);
59}
60
61MappedFile* MappedFile::Map(istream* s, const FstReadOptions &opts,
62                            size_t size) {
63  std::streampos spos = s->tellg();
64  if (opts.mode == FstReadOptions::MAP && spos >= 0 &&
65      spos % kArchAlignment == 0) {
66    size_t pos = spos;
67    int fd = open(opts.source.c_str(), O_RDONLY);
68    if (fd != -1) {
69      int pagesize = getpagesize();
70      off_t offset = pos % pagesize;
71      off_t upsize = size + offset;
72      void *map = mmap(0, upsize, PROT_READ, MAP_SHARED, fd, pos - offset);
73      char *data = reinterpret_cast<char*>(map);
74      if (close(fd) == 0 && map != MAP_FAILED) {
75        MemoryRegion region;
76        region.mmap = map;
77        region.size = upsize;
78        region.data = reinterpret_cast<void*>(data + offset);
79        MappedFile *mmf = new MappedFile(region);
80        s->seekg(pos + size, ios::beg);
81        if (s) {
82          VLOG(1) << "mmap'ed region of " << size << " at offset " << pos
83                  << " from " << opts.source.c_str() << " to addr " << map;
84          return mmf;
85        }
86        delete mmf;
87      } else {
88        LOG(INFO) << "Mapping of file failed: " << strerror(errno);
89      }
90    }
91  }
92  // If all else fails resort to reading from file into allocated buffer.
93  if (opts.mode != FstReadOptions::READ) {
94    LOG(WARNING) << "File mapping at offset " << spos << " of file "
95                 << opts.source << " could not be honored, reading instead.";
96  }
97  MappedFile* mf = Allocate(size);
98  if (!s->read(reinterpret_cast<char*>(mf->mutable_data()), size)) {
99    delete mf;
100    return NULL;
101  }
102  return mf;
103}
104
105}  // namespace fst
106