Skip to content

Commit c9ffe54

Browse files
jihoonsonwesm
authored andcommitted
ARROW-194: C++: Allow read-only memory mapped source
A simple patch to allow read-only mode. A test is also included. Author: Jihoon Son <jihoonson@apache.org> Closes #72 from jihoonson/ARROW-194 and squashes the following commits: f55dd22 [Jihoon Son] Change the type of protection flag from int8_t to int b928031 [Jihoon Son] Add missing initialization 63b99c5 [Jihoon Son] Remove unintended whitespace 22e6128 [Jihoon Son] Simplify error check 5559b8d [Jihoon Son] - Fixed a wrong protection flag in a test - Added a routine to check the protection flag before writing - Added a unit test to check the error status for protection mode - Improved failure check for mmap() d8939fa [Jihoon Son] Allow read-only memory mapped source.
1 parent 3302257 commit c9ffe54

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

cpp/src/arrow/ipc/ipc-memory-test.cc

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@
2626

2727
#include "arrow/ipc/memory.h"
2828
#include "arrow/ipc/test-common.h"
29-
#include "arrow/test-util.h"
30-
#include "arrow/util/buffer.h"
31-
#include "arrow/util/status.h"
3229

3330
namespace arrow {
3431
namespace ipc {
@@ -67,6 +64,57 @@ TEST_F(TestMemoryMappedSource, WriteRead) {
6764
}
6865
}
6966

67+
TEST_F(TestMemoryMappedSource, ReadOnly) {
68+
const int64_t buffer_size = 1024;
69+
std::vector<uint8_t> buffer(buffer_size);
70+
71+
test::random_bytes(1024, 0, buffer.data());
72+
73+
const int reps = 5;
74+
75+
std::string path = "ipc-read-only-test";
76+
CreateFile(path, reps * buffer_size);
77+
78+
std::shared_ptr<MemoryMappedSource> rwmmap;
79+
ASSERT_OK(MemoryMappedSource::Open(path, MemorySource::READ_WRITE, &rwmmap));
80+
81+
int64_t position = 0;
82+
for (int i = 0; i < reps; ++i) {
83+
ASSERT_OK(rwmmap->Write(position, buffer.data(), buffer_size));
84+
85+
position += buffer_size;
86+
}
87+
rwmmap->Close();
88+
89+
std::shared_ptr<MemoryMappedSource> rommap;
90+
ASSERT_OK(MemoryMappedSource::Open(path, MemorySource::READ_ONLY, &rommap));
91+
92+
position = 0;
93+
std::shared_ptr<Buffer> out_buffer;
94+
for (int i = 0; i < reps; ++i) {
95+
ASSERT_OK(rommap->ReadAt(position, buffer_size, &out_buffer));
96+
97+
ASSERT_EQ(0, memcmp(out_buffer->data(), buffer.data(), buffer_size));
98+
position += buffer_size;
99+
}
100+
rommap->Close();
101+
}
102+
103+
TEST_F(TestMemoryMappedSource, InvalidMode) {
104+
const int64_t buffer_size = 1024;
105+
std::vector<uint8_t> buffer(buffer_size);
106+
107+
test::random_bytes(1024, 0, buffer.data());
108+
109+
std::string path = "ipc-invalid-mode-test";
110+
CreateFile(path, buffer_size);
111+
112+
std::shared_ptr<MemoryMappedSource> rommap;
113+
ASSERT_OK(MemoryMappedSource::Open(path, MemorySource::READ_ONLY, &rommap));
114+
115+
ASSERT_RAISES(IOError, rommap->Write(0, buffer.data(), buffer_size));
116+
}
117+
70118
TEST_F(TestMemoryMappedSource, InvalidFile) {
71119
std::string non_existent_path = "invalid-file-name-asfd";
72120

cpp/src/arrow/ipc/memory.cc

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ MemorySource::~MemorySource() {}
4141

4242
class MemoryMappedSource::Impl {
4343
public:
44-
Impl() : file_(nullptr), is_open_(false), data_(nullptr) {}
44+
Impl() : file_(nullptr), is_open_(false), is_writable_(false), data_(nullptr) {}
4545

4646
~Impl() {
4747
if (is_open_) {
@@ -53,10 +53,12 @@ class MemoryMappedSource::Impl {
5353
Status Open(const std::string& path, MemorySource::AccessMode mode) {
5454
if (is_open_) { return Status::IOError("A file is already open"); }
5555

56-
path_ = path;
56+
int prot_flags = PROT_READ;
5757

5858
if (mode == MemorySource::READ_WRITE) {
5959
file_ = fopen(path.c_str(), "r+b");
60+
prot_flags |= PROT_WRITE;
61+
is_writable_ = true;
6062
} else {
6163
file_ = fopen(path.c_str(), "rb");
6264
}
@@ -73,14 +75,13 @@ class MemoryMappedSource::Impl {
7375
fseek(file_, 0L, SEEK_SET);
7476
is_open_ = true;
7577

76-
// TODO(wesm): Add read-only version of this
77-
data_ = reinterpret_cast<uint8_t*>(
78-
mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file_), 0));
79-
if (data_ == nullptr) {
78+
void* result = mmap(nullptr, size_, prot_flags, MAP_SHARED, fileno(file_), 0);
79+
if (result == MAP_FAILED) {
8080
std::stringstream ss;
8181
ss << "Memory mapping file failed, errno: " << errno;
8282
return Status::IOError(ss.str());
8383
}
84+
data_ = reinterpret_cast<uint8_t*>(result);
8485

8586
return Status::OK();
8687
}
@@ -89,11 +90,15 @@ class MemoryMappedSource::Impl {
8990

9091
uint8_t* data() { return data_; }
9192

93+
bool writable() { return is_writable_; }
94+
95+
bool opened() { return is_open_; }
96+
9297
private:
93-
std::string path_;
9498
FILE* file_;
9599
int64_t size_;
96100
bool is_open_;
101+
bool is_writable_;
97102

98103
// The memory map
99104
uint8_t* data_;
@@ -134,6 +139,9 @@ Status MemoryMappedSource::ReadAt(
134139
}
135140

136141
Status MemoryMappedSource::Write(int64_t position, const uint8_t* data, int64_t nbytes) {
142+
if (!impl_->opened() || !impl_->writable()) {
143+
return Status::IOError("Unable to write");
144+
}
137145
if (position < 0 || position >= impl_->size()) {
138146
return Status::Invalid("position is out of bounds");
139147
}

0 commit comments

Comments
 (0)