Skip to main content


Did you know that the std::istream model is pretty cool? In a few lines of code you can create your own streambuf implementation that drives the istream model.




In a custom part of code in a virtual file system i needed to take an region of the file and wanted to pass that to my code using the isteam interface. This way
i could use std::ifstream to easily test out application logic. To my surprise this is actually dead simple. I found some clues on stackoverflow that pushed me
in the right direction. The istream interface actually uses a 'streambuff' interface that holds various concepts such as buffering, underflow/overflow checks.

The most important parts of a custom std::streambuf implemention boils down actually three method calls. First the underflow/uflow functions are called part of the standard streambuf implementation. The default logic is to perform a for loop of n uflow reads until eof is return. I learned that this for-loop behaviour is because of the xsgetn function. To improve performance I added the appropriate xsgetn implementation of the routine and this should hopefully robust implementation of substreams.

In addition to the substream implementation below, you can also use this type of implementation to wrap platform specific file reading from android file systems. The nicest part is that it is easy to replace the code with another implementation as long as they share a common interface. This snippet is a keeper, that's for sure.


 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
class substreambuf : public std::streambuf
{
public:

 substreambuf(std::streambuf *sbuf, std::size_t start, std::size_t len) : m_sbuf(sbuf), m_start(start), m_len(len), m_pos(0)
 {
  std::streampos p = m_sbuf->pubseekpos(start);
  assert(p != std::streampos(-1));
 }


protected:

 std::streamsize xsgetn(char* s, std::streamsize n)
 {
  std::streamsize max_buffer = std::min(n, m_len - m_pos);
  std::streamsize ret = m_sbuf->sgetn(s, max_buffer);
  m_pos = m_pos + ret;
  return ret;
 }

 int underflow()
 {
  if (m_pos + std::streamsize(1) >= m_len)
   return traits_type::eof();
  return m_sbuf->sgetc();
 }

 int uflow()
 {
  if (m_pos + std::streamsize(1) > m_len)
   return traits_type::eof();
  m_pos += std::streamsize(1);
  return m_sbuf->sbumpc();
 }

 std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
 {
  std::streampos cursor;

  if (way == std::ios_base::beg)
   cursor = off;
  else if (way == std::ios_base::cur)
   cursor = m_pos + off;
  else if (way == std::ios_base::end)
   cursor = m_len - off;

  if (cursor < 0 || cursor >= m_len)
   return std::streampos(-1);
  m_pos = cursor;
  if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
   return std::streampos(-1);

  return m_pos;
 }

 std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
 {
  if (sp < 0 || sp >= m_len)
   return std::streampos(-1);
  m_pos = sp;
  if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
   return std::streampos(-1);
  return m_pos;
 }

 std::streamsize showmanyc()
 {
  if (m_pos > m_len)
  {
   return std::streamsize(-1);
  }
  else
  {
   return std::max(m_len - m_pos, std::streamsize(4096));
  }
 }

private:
 std::streambuf *m_sbuf;
 std::streampos m_start;
 std::streamsize m_len;
 std::streampos m_pos;
};

Comments

Popular posts from this blog

Material & shader management

In the upcoming changes in my editor I implemented the material system inspired on  Frostbite engine of DICE, binaries are download-able on the project page. Also I've implemented an conversion tool and file-format for future mesh formats using Assimp.

A visual approach to programming

It's been a while since I had opportunity to write anything, with the added misfortune of a hardware deficit but seemingly still had some backups to recover old older entries. Over the years I've taken a interest in language theory and in particular visual programming. Inspired by Unreal Kismet, CryEngine Flow and BitSquid Flow; I too set out myself of creating a similar environment. Primarily I just wanted a visual language as I believed they hold a certain productivity value. My initial designs were of a very object-orientated nature. However this approach just never felt right to me. It means you are going to increase post-deserialization time due to v-table fix-ups but it is also takes dexterity to maintain the code hierarchy required. So what I really wanted to do was design a system a) that reduces post-deserialization times to a bare minimum b) was not inheritance heavy c) small enough to be embeddable. On of the interesting methods that I considered was generating m...

Roadtrip to Germany-Switzerland-Austria-Czech pt. 1

Last month I had the luxury to go down for a road trip through various countries in Germany. Despite it being early fall season we actually had a lot of sunshine, and it was uncanny to see the beautiful scenery we passed through. Our favorite place was on the road from Salzburg to Halstadt where we were headed for the famous sky outlook. We came across a lake surrounded by mountains (presumable alps), the nature is unfathomable.