Coverage Report

Created: 2025-07-11 06:31

/src/libxmlb/src/xb-builder-source-ctx.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "XbSilo"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
13
#include "xb-builder-source-ctx-private.h"
14
#include "xb-common-private.h"
15
16
typedef struct {
17
  GFile *file;
18
  GInputStream *istream;
19
  gchar *basename;
20
  gchar *content_type;
21
} XbBuilderSourceCtxPrivate;
22
23
G_DEFINE_TYPE_WITH_PRIVATE(XbBuilderSourceCtx, xb_builder_source_ctx, G_TYPE_OBJECT)
24
0
#define GET_PRIVATE(o) (xb_builder_source_ctx_get_instance_private(o))
25
26
/**
27
 * xb_builder_source_ctx_get_stream:
28
 * @self: a #XbBuilderSourceCtx
29
 *
30
 * Returns the input stream currently being processed.
31
 *
32
 * Returns: (transfer none): a #GInputStream
33
 *
34
 * Since: 0.1.7
35
 **/
36
GInputStream *
37
xb_builder_source_ctx_get_stream(XbBuilderSourceCtx *self)
38
0
{
39
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
40
0
  g_return_val_if_fail(XB_IS_BUILDER_SOURCE_CTX(self), NULL);
41
0
  return priv->istream;
42
0
}
43
44
static GBytes *
45
_g_input_stream_read_bytes_in_chunks(GInputStream *stream,
46
             gsize count,
47
             gsize chunk_sz,
48
             GCancellable *cancellable,
49
             GError **error)
50
0
{
51
0
  g_autofree guint8 *tmp = NULL;
52
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
53
54
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
55
0
  g_return_val_if_fail(count > 0, NULL);
56
0
  g_return_val_if_fail(chunk_sz > 0, NULL);
57
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
58
59
  /* read from stream in chunks */
60
0
  tmp = g_malloc(chunk_sz);
61
0
  while (TRUE) {
62
0
    gssize sz;
63
0
    sz = g_input_stream_read(stream, tmp, chunk_sz, NULL, error);
64
0
    if (sz == 0)
65
0
      break;
66
0
    if (sz < 0)
67
0
      return NULL;
68
0
    g_byte_array_append(buf, tmp, sz);
69
0
    if (buf->len > count) {
70
0
      g_set_error(error,
71
0
            G_IO_ERROR,
72
0
            G_IO_ERROR_FAILED,
73
0
            "cannot read from fd: 0x%x > 0x%x",
74
0
            buf->len,
75
0
            (guint)count);
76
0
      return NULL;
77
0
    }
78
0
  }
79
0
  return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
80
0
}
81
82
/**
83
 * xb_builder_source_ctx_get_bytes:
84
 * @self: a #XbBuilderSourceCtx
85
 * @cancellable: a #GCancellable, or %NULL
86
 * @error: the #GError, or %NULL
87
 *
88
 * Returns the data currently being processed.
89
 *
90
 * If the #XbBuilderSourceCtx is backed by a file, the returned #GBytes may be
91
 * memory-mapped, and the backing file must not be modified until the #GBytes is
92
 * destroyed.
93
 *
94
 * Returns: (transfer full): a #GBytes
95
 *
96
 * Since: 0.1.7
97
 **/
98
GBytes *
99
xb_builder_source_ctx_get_bytes(XbBuilderSourceCtx *self, GCancellable *cancellable, GError **error)
100
0
{
101
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
102
0
  g_return_val_if_fail(XB_IS_BUILDER_SOURCE_CTX(self), NULL);
103
0
  g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL);
104
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
105
106
  /* Try mmap()ing the file first, as that avoids buffer allocation.
107
   * Note that this imposes the restriction that the backing file must not
108
   * be modified during the lifetime of the returned #GBytes. */
109
0
  if (priv->file != NULL) {
110
0
    g_autoptr(GMappedFile) mapped_file = NULL;
111
0
    g_autofree gchar *filename = g_file_get_path(priv->file);
112
113
0
    mapped_file = g_mapped_file_new(filename, FALSE, NULL);
114
0
    if (mapped_file != NULL)
115
0
      return g_mapped_file_get_bytes(mapped_file);
116
0
  }
117
118
0
  return _g_input_stream_read_bytes_in_chunks(priv->istream,
119
0
                128 * 1024 * 1024, /* 128Mb */
120
0
                32 * 1024,         /* 32Kb */
121
0
                cancellable,
122
0
                error);
123
0
}
124
125
/**
126
 * xb_builder_source_ctx_get_filename:
127
 * @self: a #XbBuilderSourceCtx
128
 *
129
 * Returns the basename of the file currently being processed.
130
 *
131
 * Returns: (transfer none) (nullable): a basename, or %NULL if unset
132
 *
133
 * Since: 0.1.7
134
 **/
135
const gchar *
136
xb_builder_source_ctx_get_filename(XbBuilderSourceCtx *self)
137
0
{
138
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
139
0
  g_return_val_if_fail(XB_IS_BUILDER_SOURCE_CTX(self), NULL);
140
0
  return priv->basename;
141
0
}
142
143
/**
144
 * xb_builder_source_ctx_get_content_type:
145
 * @self: a #XbBuilderSourceCtx
146
 * @cancellable: a #GCancellable, or %NULL
147
 * @error: the #GError, or %NULL
148
 *
149
 * Returns the content type of the input stream currently being
150
 * processed.
151
 *
152
 * Returns: (transfer full): a content type (e.g. `application/x-desktop`), or %NULL on error
153
 *
154
 * Since: 0.1.7
155
 **/
156
gchar *
157
xb_builder_source_ctx_get_content_type(XbBuilderSourceCtx *self,
158
               GCancellable *cancellable,
159
               GError **error)
160
0
{
161
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
162
0
  gsize bufsz = 0;
163
0
  guchar buf[4096] = {0x00};
164
165
0
  g_return_val_if_fail(XB_IS_BUILDER_SOURCE_CTX(self), NULL);
166
167
0
  if (G_IS_SEEKABLE(priv->istream)) {
168
0
    if (!g_input_stream_read_all(priv->istream,
169
0
               buf,
170
0
               sizeof(buf),
171
0
               &bufsz,
172
0
               cancellable,
173
0
               error))
174
0
      return NULL;
175
0
    if (!g_seekable_seek(G_SEEKABLE(priv->istream), 0, G_SEEK_SET, cancellable, error))
176
0
      return NULL;
177
0
  }
178
0
  if (bufsz > 0)
179
0
    return xb_content_type_guess(priv->basename, buf, bufsz);
180
0
  return xb_content_type_guess(priv->basename, NULL, 0);
181
0
}
182
183
/* private */
184
void
185
xb_builder_source_ctx_set_filename(XbBuilderSourceCtx *self, const gchar *basename)
186
0
{
187
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
188
0
  g_return_if_fail(XB_IS_BUILDER_SOURCE_CTX(self));
189
0
  g_return_if_fail(basename != NULL);
190
0
  g_free(priv->basename);
191
0
  priv->basename = g_strdup(basename);
192
0
}
193
194
static void
195
xb_builder_source_ctx_init(XbBuilderSourceCtx *self)
196
0
{
197
0
}
198
199
static void
200
xb_builder_source_ctx_finalize(GObject *obj)
201
0
{
202
0
  XbBuilderSourceCtx *self = XB_BUILDER_SOURCE_CTX(obj);
203
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
204
0
  g_free(priv->basename);
205
0
  g_object_unref(priv->istream);
206
0
  g_clear_object(&priv->file);
207
0
  G_OBJECT_CLASS(xb_builder_source_ctx_parent_class)->finalize(obj);
208
0
}
209
210
static void
211
xb_builder_source_ctx_class_init(XbBuilderSourceCtxClass *klass)
212
0
{
213
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
214
0
  object_class->finalize = xb_builder_source_ctx_finalize;
215
0
}
216
217
/**
218
 * xb_builder_source_ctx_new:
219
 * @file: (transfer none) (nullable): Path to the file which contains the source,
220
 *    or %NULL if the source is not directly loadable from disk
221
 * @istream: (transfer none): Input stream to load the source from
222
 *
223
 * Creates a new builder source_ctx.
224
 *
225
 * Returns: (transfer full): a new #XbBuilderSourceCtx
226
 *
227
 * Since: 0.1.7
228
 **/
229
XbBuilderSourceCtx *
230
xb_builder_source_ctx_new(GFile *file, GInputStream *istream)
231
0
{
232
0
  XbBuilderSourceCtx *self = g_object_new(XB_TYPE_BUILDER_SOURCE_CTX, NULL);
233
0
  XbBuilderSourceCtxPrivate *priv = GET_PRIVATE(self);
234
235
0
  g_return_val_if_fail(file == NULL || G_IS_FILE(file), NULL);
236
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(istream), NULL);
237
238
0
  priv->file = (file != NULL) ? g_object_ref(file) : NULL;
239
0
  priv->istream = g_object_ref(istream);
240
0
  return self;
241
0
}