+/* Our stream IO notifier callback. */
+
+static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status,
+ void *context)
+{
+ SilcPacketStream ps = context;
+ int ret;
+
+ silc_mutex_lock(ps->lock);
+
+ if (ps->destroyed) {
+ silc_mutex_unlock(ps->lock);
+ return;
+ }
+
+ switch (status) {
+
+ case SILC_STREAM_CAN_WRITE:
+ if (!silc_buffer_headlen(&ps->outbuf)) {
+ silc_mutex_unlock(ps->lock);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Writing pending data to stream"));
+
+ /* Write pending data to stream */
+ while (silc_buffer_len(&ps->outbuf) > 0) {
+ ret = silc_stream_write(ps->stream, ps->outbuf.data,
+ silc_buffer_len(&ps->outbuf));
+ if (ret == 0) {
+ /* EOS */
+ silc_buffer_reset(&ps->outbuf);
+ silc_mutex_unlock(ps->lock);
+ SILC_PACKET_CALLBACK_EOS(ps);
+ return;
+ }
+
+ if (ret == -2) {
+ /* Error */
+ silc_buffer_reset(&ps->outbuf);
+ silc_mutex_unlock(ps->lock);
+ SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_WRITE);
+ return;
+ }
+
+ if (ret == -1) {
+ /* Cannot write now, write later. */
+ silc_mutex_unlock(ps->lock);
+ return;
+ }
+
+ /* Wrote data */
+ silc_buffer_pull(&ps->outbuf, ret);
+ }
+
+ silc_buffer_reset(&ps->outbuf);
+
+ silc_mutex_unlock(ps->lock);
+ break;
+
+ case SILC_STREAM_CAN_READ:
+ /* Packet receiving can only happen in one thread, so locking is not
+ required in packet receiving procedure. */
+ silc_mutex_unlock(ps->lock);
+
+ SILC_LOG_DEBUG(("Reading data from stream"));
+
+ /* Make sure we have fair amount of free space in inbuf */
+ if (silc_buffer_taillen(&ps->inbuf) < SILC_PACKET_DEFAULT_SIZE)
+ if (!silc_buffer_realloc(&ps->inbuf, silc_buffer_truelen(&ps->inbuf) +
+ SILC_PACKET_DEFAULT_SIZE * 2))
+ return;
+
+ /* Read data from stream */
+ ret = silc_stream_read(ps->stream, ps->inbuf.tail,
+ silc_buffer_taillen(&ps->inbuf));
+
+ if (ret == 0) {
+ /* EOS */
+ silc_buffer_reset(&ps->inbuf);
+ SILC_PACKET_CALLBACK_EOS(ps);
+ return;
+ }
+
+ if (ret == -2) {
+ /* Error */
+ silc_buffer_reset(&ps->inbuf);
+ SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ);
+ return;
+ }
+
+ if (ret == -1) {
+ /* Cannot read now, do it later. */
+ silc_buffer_pull(&ps->inbuf, silc_buffer_len(&ps->inbuf));
+ return;
+ }
+
+ /* Read some data */
+ silc_buffer_pull_tail(&ps->inbuf, ret);
+
+ /* Now process the data */
+ silc_packet_read_process(ps);
+
+ break;
+
+ default:
+ silc_mutex_unlock(ps->lock);
+ break;
+ }
+}
+
+/* Allocate packet */
+
+static SilcPacket silc_packet_alloc(SilcPacketEngine engine)
+{
+ SilcPacket packet;
+
+ SILC_LOG_DEBUG(("Packet pool count %d",
+ silc_list_count(engine->packet_pool)));
+
+ silc_mutex_lock(engine->lock);
+
+ /* Get packet from freelist or allocate new one. */
+ packet = silc_list_get(engine->packet_pool);
+ if (!packet) {
+ void *tmp;
+
+ silc_mutex_unlock(engine->lock);
+
+ packet = silc_calloc(1, sizeof(*packet));
+ if (!packet)
+ return NULL;
+
+ SILC_LOG_DEBUG(("Allocating new packet %p", packet));
+
+ tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE);
+ if (!tmp) {
+ silc_free(packet);
+ return NULL;
+ }
+ silc_buffer_set(&packet->buffer, tmp, SILC_PACKET_DEFAULT_SIZE);
+ silc_buffer_reset(&packet->buffer);
+
+ return packet;
+ }
+
+ SILC_LOG_DEBUG(("Get packet %p", packet));
+
+ /* Delete from freelist */
+ silc_list_del(engine->packet_pool, packet);
+
+ silc_mutex_unlock(engine->lock);
+
+ return packet;
+}
+
+
+/******************************** Packet API ********************************/