input_file.c 4.15 KB
Newer Older
1
#define _XOPEN_SOURCE 600
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "input_file.h"

#include "cdi.h"
#include "dmemory.h"

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

static void cdiInputFile_destruct(CdiInputFile* me);

//For an explanation of the condestruct() pattern, see the comment in iterator_grib.c
//path != NULL -> construction
//path = NULL -> destruction
18
static CdiInputFile* cdiInputFile_condestruct(CdiInputFile* me, const char* path)
19
20
21
22
{
  #define super() (&me->super)
  if(!path) goto destruct;
  cdiRefObject_construct(super());
23
  me->path = strdup(path);
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
  if(!me->path) goto destructSuper;
  do
    {
      me->fileDescriptor = open(me->path, O_RDONLY);
    }
  while(me->fileDescriptor == -1 && (errno == EINTR || errno == EAGAIN));
  if(me->fileDescriptor == -1) goto freePath;
  //construction successfull, now we can set our own destructor
  super()->destructor = (void(*)(CdiReferencedObject*))cdiInputFile_destruct;
  goto success;

// ^        constructor code       ^
// |                               |
// v destructor/error-cleanup code v

destruct:
  close(me->fileDescriptor);
freePath:
  free(me->path);
destructSuper:
  cdiRefObject_destruct(super());
  me = NULL;

success:
  return me;
  #undef super
}

static CdiInputFile** openFileList = NULL;
static size_t openFileCount = 0, openFileListSize = 0;
static pthread_mutex_t openFileListLock = PTHREAD_MUTEX_INITIALIZER;

//This either returns a new object, or retains and returns a preexisting open file.
CdiInputFile* cdiInputFile_make(const char* path)
{
  CdiInputFile* result = NULL;
  xassert(path);
  int error = pthread_mutex_lock(&openFileListLock);
  xassert(!error);
    {
      //Check the list of open files for the given path.
      for(size_t i = openFileCount; i-- && !result; )
        {
          if(!strcmp(path, openFileList[i]->path)) result = openFileList[i];
        }
      //If no open file was found, we open one, otherwise we just retain the existing one one more time.
      if(result)
        {
          cdiRefObject_retain(&result->super);
        }
      else
        {
76
          result = (CdiInputFile *)xmalloc(sizeof(*result));
77
          if(!cdiInputFile_condestruct(result, path))
78
79
80
81
82
83
84
85
86
87
88
89
            {
              //An error occured during construction, avoid a memory leak.
              free(result);
              result = NULL;
            }
          else
            {
              //Add the new file to the list of open files.
              if(openFileCount == openFileListSize)
                {
                  openFileListSize *= 2;
                  if(openFileListSize < 16) openFileListSize = 16;
90
                  openFileList = (CdiInputFile **)xrealloc(openFileList, openFileListSize);
91
92
93
94
95
96
97
98
99
100
101
102
103
                }
              xassert(openFileCount < openFileListSize);
              openFileList[openFileCount++] = result;
            }
        }
    }
  error = pthread_mutex_unlock(&openFileListLock);
  xassert(!error);
  return result;
}

int cdiInputFile_read(const CdiInputFile* me, off_t readPosition, size_t readSize, size_t* outActualReadSize, void* buffer)
{
104
  char* byteBuffer = (char *)buffer;
105
106
107
108
109
110
111
112
113
114
  size_t trash;
  if(!outActualReadSize) outActualReadSize = &trash;
  *outActualReadSize = 0;
  while(readSize)
    {
      ssize_t bytesRead = pread(me->fileDescriptor, byteBuffer, readSize, readPosition);
      if(bytesRead == -1) return (errno == EINVAL) ?  CDI_EINVAL : CDI_ESYSTEM;
      if(bytesRead == 0) return CDI_EEOF;
      byteBuffer += bytesRead;
      readPosition += bytesRead;
115
116
      readSize -= (size_t)bytesRead;
      *outActualReadSize += (size_t)bytesRead;
117
118
119
120
    }
  return CDI_NOERR;
}

121
const char* cdiInputFile_getPath(const CdiInputFile* me)
122
{
123
  return me->path;
124
125
126
127
128
129
130
131
}

void cdiInputFile_destruct(CdiInputFile* me)
{
  int error = pthread_mutex_lock(&openFileListLock);
  xassert(!error);
    {
      //Find the position of me in the list of open files.
132
133
      ssize_t position = (ssize_t)openFileCount;
      while (position > 0 && openFileList[--position] != me);
134
135
136
137
138
      //Remove me from the list
      openFileList[position] = openFileList[--openFileCount];
    }
  error = pthread_mutex_unlock(&openFileListLock);
  xassert(!error);
139
  cdiInputFile_condestruct(me, NULL);
140
}