summaryrefslogtreecommitdiff
path: root/cpp/src/IceGrid/FileCache.cpp
blob: f6cbce8126447bde5cddce1217da36aa8645cc95 (plain)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

#include <Ice/Communicator.h>
#include <Ice/Properties.h>

#include <IceGrid/FileCache.h>
#include <IceGrid/Exception.h>

#include <deque>
#include <fstream>

using namespace std;
using namespace IceGrid;

FileCache::FileCache(const Ice::CommunicatorPtr& communicator) : 
    _messageSizeMax(communicator->getProperties()->getPropertyAsIntWithDefault("Ice.MessageSizeMax", 1024) * 1024)
{
}

Ice::Long
FileCache::getOffsetFromEnd(const string& file, int originalCount)
{
    ifstream is(file.c_str());
    if(is.fail())
    {
	throw FileNotAvailableException("failed to open file `" + file + "'");
    }

    int blockSize = 16 * 1024; // Start reading a block of 16K from the end of the file.
    is.seekg(0, ios::end);
    Ice::Long endOfFileOffset = is.tellg();
    Ice::Long lastBlockOffset = endOfFileOffset;
    int totalCount = 0;
    int totalSize = 0;
    string line;
    do
    {
	//
	// Move the current position of the stream to the new block to
	// read.
	//
	is.clear();
	if(lastBlockOffset - blockSize > 0)
	{
#ifdef _WIN32
	    is.seekg(static_cast<int>(lastBlockOffset - blockSize));
#else
	    is.seekg(static_cast<streampos>(lastBlockOffset - blockSize));
#endif
	    getline(is, line); // Ignore the first line as it's most likely not complete.
	}
	else
	{
	    is.seekg(0); // We've reach the begining of the file.
	}
	
	//
	// Read the block and count the number of lines in the block
	// as well as the number of bytes read. If we found the "first
	// last N line", we start throwing out the lines read at the
	// begining of the file. The total size read will give us the
	// position from the end of the file.
	//
	deque<string> lines;
	int count = originalCount - totalCount; // Number of lines left to find.
	while(is.good() && is.tellg() < lastBlockOffset)
	{
	    getline(is, line);

	    lines.push_back(line);
	    ++totalCount;
	    totalSize += line.size() + 1;
	    if(lines.size() == static_cast<unsigned int>(count + 1))
	    {
		--totalCount;
		totalSize -= lines.front().size() + 1;
		lines.pop_front();
	    }
	}

	if(lastBlockOffset - blockSize < 0)
	{
	    break; // We're done if the block started at the begining of the file.
	}
	else if(totalCount < originalCount)
	{
	    //
	    // Otherwise, it we still didn't find the required number of lines, 
	    // read another block of text before this block.
	    //
	    lastBlockOffset -= blockSize; // Position of the block we just read.
	    blockSize *= 2; // Read a bigger block.
	}
    }
    while(totalCount  < originalCount && !is.bad());

    if(is.bad())
    {
	throw FileNotAvailableException("unrecoverable error occured while reading file `" + file + "'");
    }

    return endOfFileOffset - totalSize;
}

bool
FileCache::read(const string& file, Ice::Long offset, int size, Ice::Long& newOffset, Ice::StringSeq& lines)
{
    assert(size > 0);

    if(size > _messageSizeMax)
    {
	size = _messageSizeMax;
    }

    ifstream is(file.c_str());
    if(is.fail())
    {
	throw FileNotAvailableException("failed to open file `" + file + "'");
    }

    //
    // Check if the requested offset is past the end of the file, if
    // that's the case return an empty sequence of lines and indicate
    // the EOF.
    //
    is.seekg(0, ios::end);
    if(offset >= is.tellg())
    {
	newOffset = is.tellg();
	lines = Ice::StringSeq();
	return true;
    }

    //
    // Read lines from the file until we read enough or reached EOF.
    // 
    newOffset = offset;
    lines = Ice::StringSeq();
#ifdef _WIN32
    is.seekg(static_cast<int>(offset));
#else
    is.seekg(static_cast<streampos>(offset));
#endif
    int totalSize = 1024; // Some room for the message header.
    string line;
    for(int i = 0; is.good() && totalSize < size; ++i)
    {
	getline(is, line);
	totalSize += line.size() + 5; // 5 bytes for the encoding of the string size (worst case scenario)
	if(!is.fail())
	{
	    newOffset = is.tellg();
	}
	lines.push_back(line);
    }

    if(is.bad())
    {
	throw FileNotAvailableException("unrecoverable error occured while reading file `" + file + "'");
    }

    return is.eof();
}