source: rtems/cpukit/libdl/rtl-obj-cache.c @ 21275b58

5
Last change on this file since 21275b58 was f59d435d, checked in by Chris Johns <chrisj@…>, on 04/12/18 at 07:46:49

libdl: Remove _t from all structures as this is reserved for the standards

  • Property mode set to 100644
File size: 6.8 KB
Line 
1/*
2 *  COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org>
3 *
4 *  The license and distribution terms for this file may be
5 *  found in the file LICENSE in this distribution or at
6 *  http://www.rtems.org/license/LICENSE.
7 */
8/**
9 * @file
10 *
11 * @ingroup rtems_rtl
12 *
13 * @brief RTEMS Run-Time Linker Object File cache buffers a section of the
14 *        object file in a buffer to localise read performance.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <errno.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h>
25#include <inttypes.h>
26#include <rtems/inttypes.h>
27
28#include <rtems/rtl/rtl-allocator.h>
29#include <rtems/rtl/rtl-obj-cache.h>
30#include "rtl-error.h"
31#include <rtems/rtl/rtl-trace.h>
32
33bool
34rtems_rtl_obj_cache_open (rtems_rtl_obj_cache* cache, size_t size)
35{
36  cache->fd        = -1;
37  cache->file_size = 0;
38  cache->offset    = 0;
39  cache->size      = size;
40  cache->level     = 0;
41  cache->buffer    = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, false);
42  if (!cache->buffer)
43  {
44    rtems_rtl_set_error (ENOMEM, "no memory for cache buffer");
45    return false;
46  }
47  return true;
48}
49
50void
51rtems_rtl_obj_cache_close (rtems_rtl_obj_cache* cache)
52{
53  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
54    printf ("rtl: cache: %2d: close\n", cache->fd);
55  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, cache->buffer);
56  cache->buffer    = NULL;
57  cache->fd        = -1;
58  cache->file_size = 0;
59  cache->level     = 0;
60}
61
62void
63rtems_rtl_obj_cache_flush (rtems_rtl_obj_cache* cache)
64{
65  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
66    printf ("rtl: cache: %2d: flush\n", cache->fd);
67  cache->fd        = -1;
68  cache->file_size = 0;
69  cache->offset    = 0;
70  cache->level     = 0;
71}
72
73bool
74rtems_rtl_obj_cache_read (rtems_rtl_obj_cache* cache,
75                          int                  fd,
76                          off_t                offset,
77                          void**               buffer,
78                          size_t*              length)
79{
80  struct stat sb;
81
82  if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
83    printf ("rtl: cache: %2d: fd=%d offset=%" PRIdoff_t "length=%zu area=[%"
84            PRIdoff_t ",%" PRIdoff_t "] cache=[%" PRIdoff_t ",%" PRIdoff_t "] size=%zu\n",
85            fd, cache->fd, offset, *length,
86            offset, offset + *length,
87            cache->offset, cache->offset + cache->level,
88            cache->file_size);
89
90  if (*length > cache->size)
91  {
92    rtems_rtl_set_error (EINVAL, "read size larger than cache size");
93    return false;
94  }
95
96  if (cache->fd == fd)
97  {
98    if (offset > cache->file_size)
99    {
100      rtems_rtl_set_error (EINVAL, "offset past end of file: offset=%i size=%i",
101                           (int) offset, (int) cache->file_size);
102      return false;
103    }
104
105    /*
106     * We sometimes are asked to read strings of a length we do not know.
107     */
108    if ((offset + *length) > cache->file_size)
109    {
110      *length = cache->file_size - offset;
111      if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
112        printf ("rtl: cache: %2d: truncate length=%d\n", fd, (int) *length);
113    }
114  }
115
116  while (true)
117  {
118    size_t buffer_offset = 0;
119    size_t buffer_read = cache->size;
120
121    /*
122     * Is the data in the cache for this file ?
123     */
124    if (fd == cache->fd)
125    {
126      /*
127       * Do not read past the end of the file.
128       */
129      if ((offset + buffer_read) > cache->file_size)
130        buffer_read = cache->file_size - offset;
131
132      /*
133       * Is any part of the data in the cache ?
134       */
135      if ((offset >= cache->offset) &&
136          (offset < (cache->offset + cache->level)))
137      {
138        size_t size;
139
140        buffer_offset = offset - cache->offset;
141        size          = cache->level - buffer_offset;
142
143        /*
144         * Return the location of the data in the cache.
145         */
146        *buffer = cache->buffer + buffer_offset;
147
148        /*
149         * Is all the data in the cache or just a part ?
150         */
151        if (*length <= size)
152          return true;
153
154        if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
155          printf ("rtl: cache: %2d: copy-down: buffer_offset=%d size=%d level=%d\n",
156                  fd, (int) buffer_offset, (int) size, (int) cache->level);
157
158        /*
159         * Copy down the data in the buffer and then fill the remaining space
160         * with as much data we are able to read.
161         */
162        memmove (cache->buffer, cache->buffer + buffer_offset, size);
163
164        cache->offset = offset;
165        cache->level  = size;
166        buffer_read   = cache->size - cache->level;
167        buffer_offset = size;
168
169        /*
170         * Do not read past the end of the file.
171         */
172        if ((offset + buffer_offset + buffer_read) > cache->file_size)
173          buffer_read = cache->file_size - (offset + buffer_offset);
174      }
175    }
176
177    if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
178      printf ("rtl: cache: %2d: seek: offset=%" PRIdoff_t "buffer_offset=%zu"
179              "read=%zu cache=[%" PRIdoff_t ",%" PRIdoff_t "] "
180              "dist=%" PRIdoff_t "\n",
181              fd, offset + buffer_offset, buffer_offset, buffer_read,
182              offset, offset + buffer_read,
183              (cache->file_size - offset));
184
185    if (lseek (fd, offset + buffer_offset, SEEK_SET) < 0)
186    {
187      rtems_rtl_set_error (errno, "file seek failed");
188      return false;
189    }
190
191    /*
192     * Loop reading the data from the file until either an error or 0 is
193     * returned and if data has been read check if the amount is what we
194     * want. If not it is an error. A POSIX read can read data in fragments.
195     */
196
197    cache->level = buffer_offset + buffer_read;
198
199    while (buffer_read)
200    {
201      int r = read (fd, cache->buffer + buffer_offset, buffer_read);
202      if (r < 0)
203      {
204        rtems_rtl_set_error (errno, "file read failed");
205        return false;
206      }
207      if ((r == 0) && buffer_read)
208      {
209        if (rtems_rtl_trace (RTEMS_RTL_TRACE_CACHE))
210          printf ("rtl: cache: %2d: read: past end by=%d\n", fd, (int) buffer_read);
211        cache->level = cache->level - buffer_read;
212        buffer_read = 0;
213      }
214      else
215      {
216        buffer_read -= r;
217        buffer_offset += r;
218      }
219    }
220
221    cache->offset = offset;
222
223    if (cache->fd != fd)
224    {
225      cache->fd = fd;
226
227      if (fstat (cache->fd, &sb) < 0)
228      {
229        rtems_rtl_set_error (errno, "file stat failed");
230        return false;
231      }
232
233      cache->file_size = sb.st_size;
234    }
235  }
236
237  return false;
238}
239
240bool
241rtems_rtl_obj_cache_read_byval (rtems_rtl_obj_cache* cache,
242                                int                  fd,
243                                off_t                offset,
244                                void*                buffer,
245                                size_t               length)
246{
247  void*  cbuffer = 0;
248  size_t len = length;
249  bool   ok = rtems_rtl_obj_cache_read (cache, fd, offset, &cbuffer, &len);
250  if (ok && (len != length))
251    ok = false;
252  if (ok)
253    memcpy (buffer, cbuffer, length);
254  return ok;
255}
Note: See TracBrowser for help on using the repository browser.