summaryrefslogtreecommitdiff
path: root/euler/source/euler/stream.cpp
blob: 950f3c512e89d67af98ee4a8cd4047976f222f7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <euler/stream.hpp>
#include <cstring>

namespace euler {

  stream::~stream() {}

  void file_stream::write_buffer() {
    if (__euler_write_to_stream(handle, buffer_size, buffer)
        != __EULER_SR_SUCCESS)
      good = false;//TODO: more precise error reporting
    else
      buffer_dirty = false;
  }

  file_stream::file_stream(
    __euler_stream_handle handle, bool is_readable, bool is_writable,
    bool clear, bool seek_to_end)

  : handle(handle), is_readable(is_readable), is_writable(is_writable),
    buffer(0), good(true) {

    if (clear) {
      if (__euler_set_stream_length(handle, 0) != __EULER_SR_SUCCESS) {
        good = false;
        return;
      }
      length = 0;
    }

    else if (__euler_get_stream_length(handle, length) != __EULER_SR_SUCCESS) {
      good = false;
      return;
    }

    if (seek_to_end) {
      if (__euler_seek_stream(handle, __EULER_SF_END, 0)
          != __EULER_SR_SUCCESS) {
        good = false;
        return;
      }
      offset = length;
    }

    else
      offset = 0;

  }

  file_stream::~file_stream() {
    if (buffer) {
      if (buffer_dirty)
        write_buffer();
      delete[] buffer;
    }
    __euler_close_stream(handle);
  }

  bool file_stream::try_read(void *into, uint64_t bytes) {

    if (bytes == 0)
      return true;

    if (offset + bytes > length)
      return false;

    if (buffer) {
      uint64_t start = offset > buffer_offset ? offset : buffer_offset;
      uint64_t end = offset + bytes < buffer_offset + buffer_size
              ? offset + bytes : buffer_offset + buffer_size;
      if (end > start) {

        uint64_t real_end = offset + bytes;

        std::memcpy(
          (uint8_t *)into + start - offset, buffer + start - buffer_offset,
          end - start);
        if (start != offset)
          if (!try_read(into, start - offset))
            return false;

        if (end != real_end) {
          if (!try_seek(__EULER_SF_BEGINNING, end))
            return false;
          if (!try_read((uint8_t *)into + end, real_end - end))
            return false;
        }

        else if (offset != real_end)
          if (!try_seek(__EULER_SF_BEGINNING, real_end))
            return false;

        return true;

      }
    }

    if (buffer_dirty) {
      write_buffer();
      if (!good)
        return false;
    }

    //1024 is arbitrary
    buffer_size = length - offset < 1024 ? length - offset : 1024;

    if (buffer != 0)
      delete[] buffer;

    buffer = new uint8_t[buffer_size];
    buffer_dirty = false;
    buffer_offset = offset;

    if (__euler_read_from_stream(handle, buffer_size, buffer)
        != __EULER_SR_SUCCESS) {
      delete[] buffer;
      buffer = 0;
      return false;
    }

    uint64_t read_this_buffer = buffer_size < bytes ? buffer_size : bytes;
    std::memcpy(into, buffer, read_this_buffer);
    offset += read_this_buffer;

    if (read_this_buffer == bytes)
      return true;

    return try_read(
      (uint8_t *)into + read_this_buffer, bytes - read_this_buffer);

  }

  bool file_stream::try_seek(__euler_seek_from from, int64_t offset) {
    if (__euler_seek_stream(handle, from, offset) != __EULER_SR_SUCCESS)
      return false;
    switch (from) {
    case __EULER_SF_BEGINNING:
      this->offset = offset;
      return true;
    case __EULER_SF_END:
      this->offset = length + offset;
      return true;
    case __EULER_SF_CURRENT_POSITION:
      this->offset += offset;
      return true;
    default:
      return false;
    }
  }

}