aboutsummaryrefslogtreecommitdiffstats
path: root/libglouglou/sendbuf.c
blob: 15a7d23b01ef895e21360edcdc9b40c7e9e1afcd (plain) (blame)
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
#include <stdlib.h>
#include <string.h>

#include "sendbuf.h"

static int flushbuf(struct sendbuf *);
static void cb_timer(evutil_socket_t, short, void *);

/*
 * Public
 */

/*
 * Create a sendbuf
 */
struct sendbuf *
sendbuf_new(struct event_base *ev_base, int buffer_size, int msec_max,
            int (*send_func)(void *, int, void *), void *usrdata)
{
  struct sendbuf *sbuf = NULL;
  struct event *ev_timer;

  sbuf = calloc(1, sizeof(struct sendbuf));
  if (!sbuf)
    return NULL;
  sbuf->ev_base = ev_base;
  sbuf->msec_max = msec_max;
  sbuf->buffer_size = buffer_size;
  sbuf->send_func = send_func;
  sbuf->usrdata = usrdata;
  sbuf->buffer = malloc(sbuf->buffer_size);
  if (!sbuf->buffer)
    goto err;

  ev_timer = evtimer_new(ev_base, cb_timer, sbuf);
  sbuf->ev_timer = ev_timer;
  sbuf->ev_timer_tv.tv_usec = msec_max * 1000;
  evtimer_add(ev_timer, &sbuf->ev_timer_tv);

  return sbuf;

err:
  sendbuf_free(sbuf);
  return NULL;
}

void
sendbuf_free(struct sendbuf *sbuf)
{
  if (sbuf->ev_timer)
    event_del(sbuf->ev_timer);
  if (sbuf->buffer && sbuf->send_func)
    flushbuf(sbuf);
  if (sbuf->buffer)
    free(sbuf->buffer);
  free(sbuf);
}

/*
 * Append to the token buffer data to be sent
 * uses a memcpy, in contrary to sendbuf_gettoken().
 * return size on success, -1 on error
 */
int
sendbuf_append(struct sendbuf *sbuf, void *token, int size)
{
  if (sbuf->buffer_pos + size >= sbuf->buffer_size)
    if (flushbuf(sbuf) == -1)
      return -1;

  memcpy(sbuf->buffer + sbuf->buffer_pos, token, size);
  sbuf->buffer_pos = sbuf->buffer_pos + size;

  return size;
}

/*
 * Returns a token buffer to write data to be sent
 * avoids memcpy, in contrary to sendbuf_append().
 * might return NULL if the sendbuf is temporary full
 */
void *
sendbuf_gettoken(struct sendbuf *sbuf, int size)
{
  void *token;

  if (sbuf->buffer_pos + size >= sbuf->buffer_size)
    if (flushbuf(sbuf) == -1)
      return NULL;

  token = sbuf->buffer + sbuf->buffer_pos;
  sbuf->buffer_pos = sbuf->buffer_pos + size;

  return token;
}

/*
 * Private
 */

/*
 * Note that you can still add data to the buffer even if flushing is in
 * progress
 * returns 0 on success or -1 on error
 */
static int
flushbuf(struct sendbuf *sbuf)
{
  int tosend, sent;

  if (sbuf->buffer_pos == 0)
    return 0;

  sbuf->flushing = 1;

  tosend = sbuf->buffer_pos - sbuf->flushing_pos;
  sent = sbuf->send_func(sbuf->buffer + sbuf->flushing_pos,
                         tosend, sbuf->usrdata);
  if (sent == -1) {
    // XXX handle error
    return -1;
  } else if (sent < tosend) {
    sbuf->flushing_pos = sbuf->flushing_pos + sent;
    return -1;
  }
  sbuf->buffer_pos = 0;

  sbuf->flushing = 0;
  sbuf->flushing_pos = 0;
  return 0;
}

static void
cb_timer(evutil_socket_t fd, short what, void *arg)
{
  struct sendbuf *sbuf;

  sbuf = arg;
  flushbuf(sbuf);
  evtimer_add(sbuf->ev_timer, &sbuf->ev_timer_tv);
}