text:
package hrd

import (
	. "github.com/101loops/bdd"
	"github.com/101loops/hrd/internal"
	"github.com/101loops/hrd/internal/types"
)

var _ = Describe("Deleter", func() {

	BeforeEach(func() {
		dsDelete = func(_ *types.Kind, _ interface{}, _ bool) error {
			panic("unexpected call")
		}
		dsDeleteKeys = func(_ *types.Kind, _ ...*types.Key) error {
			panic("unexpected call")
		}
	})

	AfterEach(func() {
		dsDelete = internal.Delete
		dsDeleteKeys = internal.DeleteKeys
	})

	It("should delete an entity by key", func() {
		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(myKind.NewNumKeys(42)))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).Key(myKind.NewNumKey(42))
	})

	It("should delete multiple entities by key", func() {
		hrdKeys := []*Key{myKind.NewNumKey(1), myKind.NewNumKey(2)}

		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(hrdKeys))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).Keys(hrdKeys)
	})

	It("should delete an entity by numeric id", func() {
		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(myKind.NewNumKeys(42)))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).ID(42)
	})

	It("should delete multiple entities by numeric id", func() {
		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(myKind.NewNumKeys(1, 2)))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).IDs(1, 2)
	})

	It("should delete an entity by text id", func() {
		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(myKind.NewTextKeys("a")))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).TextID("a")
	})

	It("should delete multiple entities by text id", func() {
		dsDeleteKeys = func(kind *types.Kind, keys ...*types.Key) error {
			Check(keys, Equals, toInternalKeys(myKind.NewTextKeys("a", "z")))
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).TextIDs("a", "z")
	})

	It("should delete an entity", func() {
		entity := &MyModel{}

		dsDelete = func(kind *types.Kind, src interface{}, multi bool) error {
			Check(multi, IsFalse)
			Check(src, Equals, entity)
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).Entity(entity)
	})

	It("should delete multiple entities", func() {
		entities := []*MyModel{&MyModel{}, &MyModel{}}

		dsDelete = func(kind *types.Kind, srcs interface{}, multi bool) error {
			Check(multi, IsTrue)
			Check(srcs, Equals, entities)
			Check(kind.Name, Equals, "my-kind")
			return nil
		}

		myKind.Delete(ctx).Entities(entities)
	})
})

redpajama_set_name: RedPajamaGithub


text:
/// <reference path="../..//app/app.authService.ts" />

/// <reference path="../..//app/app.base64Service.ts" />

/// <reference path="../..//app/app.controller.ts" />

/// <reference path="../..//app/app.dashboardController.ts" />

/// <reference path="../..//app/app.interceptor.ts" />

/// <reference path="../..//app/app.loginController.ts" />

/// <reference path="../..//app/app.mainview.ts" />

/// <reference path="../..//app/app.moduleLoader.ts" />

/// <reference path="../..//app/app.moduleProvider.ts" />

/// <reference path="../..//app/app.routes.ts" />

/// <reference path="../..//app/main.ts" />

/// <reference path="../..//app/typings/tsd.d.ts" />

/// <reference path="../..//app/modules/home/home.controller.ts" />

/// <reference path="../..//app/modules/home/index.ts" />

/// <reference path="../..//app/modules/list/index.ts" />

/// <reference path="../..//app/modules/list/list.controller.ts" />

/// <reference path="../..//app/typings/angularjs/angular-mocks.d.ts" />

/// <reference path="../..//app/typings/angularjs/angular-route.d.ts" />

/// <reference path="../..//app/typings/angularjs/angular.d.ts" />

/// <reference path="../..//app/typings/chai/chai.d.ts" />

/// <reference path="../..//app/typings/jquery/jquery.d.ts" />

/// <reference path="../..//app/typings/node/node.d.ts" />

/// <reference path="../..//app/typings/requirejs/require.d.ts" />

/// <reference path="../..//app/typings/underscore/underscore.d.ts" />

/// <reference path="../..//app/typings/webpack/webpack.d.ts" />

//}


text:
package java.nio;

import java.io.FileDescriptor;
import java.nio.channels.FileChannel;

/**
 * @hide internal use only
 */
public final class NioUtils {
    private NioUtils() {
    }

    /**
     * Gets the start address of a direct buffer.
     *

     * This method corresponds to the JNI function:
     *
     *

     *    void* GetDirectBufferAddress(JNIEnv* env, jobject buf);
     *

     *
     * @param buf
     *            the direct buffer whose address shall be returned must not be
     *            null.
     * @return the address of the buffer given, or zero if the buffer is not a
     *         direct Buffer.
     */
    public static int getDirectBufferAddress(Buffer buffer) {
        return buffer.effectiveDirectAddress;
    }

    public static void freeDirectBuffer(ByteBuffer buffer) {
        if (buffer == null) {
            return;
        }
        if (buffer instanceof DirectByteBuffer) {
            ((DirectByteBuffer) buffer).free();
        } else if (buffer instanceof MappedByteBuffer) {
            ((MappedByteBufferAdapter) buffer).free();
        } else {
            throw new AssertionError();
        }
    }

    /**
     * Returns the int file descriptor from within the given FileChannel 'fc'.
     */
    public static FileDescriptor getFD(FileChannel fc) {
        return ((FileChannelImpl) fc).getFD();
    }

    /**
     * Helps bridge between io and nio.
     */
    public static FileChannel newFileChannel(Object stream, FileDescriptor fd, int mode) {
        return new FileChannelImpl(stream, fd, mode);
    }

    /**
     * Exposes the array backing a non-direct ByteBuffer, even if the ByteBuffer is read-only.
     * Normally, attempting to access the array backing a read-only buffer throws.
     */
    public static byte[] unsafeArray(ByteBuffer b) {
        return ((HeapByteBuffer) b).backingArray;
    }

    /**
     * Exposes the array offset for the array backing a non-direct ByteBuffer,
     * even if the ByteBuffer is read-only.
     */
    public static int unsafeArrayOffset(ByteBuffer b) {
        return ((HeapByteBuffer) b).offset;
    }
}


text:
// -*- c++ -*-
// Generated by gmmproc 2.44.0 -- DO NOT MODIFY!
#ifndef _GTKMM_LINKBUTTON_P_H
#define _GTKMM_LINKBUTTON_P_H

#include <gtkmm/private/button_p.h>

#include <glibmm/class.h>

namespace Gtk
{

class LinkButton_Class : public Glib::Class
{
public:
#ifndef DOXYGEN_SHOULD_SKIP_THIS
  typedef LinkButton CppObjectType;
  typedef GtkLinkButton BaseObjectType;
  typedef GtkLinkButtonClass BaseClassType;
  typedef Gtk::Button_Class CppClassParent;
  typedef GtkButtonClass BaseClassParent;

  friend class LinkButton;
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

  const Glib::Class& init();

  static void class_init_function(void* g_class, void* class_data);

  static Glib::ObjectBase* wrap_new(GObject*);

protected:

  //Callbacks (default signal handlers):
  //These will call the *_impl member methods, which will then call the existing default signal callbacks, if any.
  //You could prevent the original default signal handlers being called by overriding the *_impl method.
  static gboolean activate_link_callback(GtkLinkButton* self);

  //Callbacks (virtual functions):
};

} // namespace Gtk

#endif /* _GTKMM_LINKBUTTON_P_H */


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.servicenetworking.v1beta.model;

/**
 * Selects and configures the service controller used by the service. The service controller handles
 * two things: - **What is allowed:** for each API request, Chemist checks the project status,
 * activation status, abuse status, billing status, service status, location restrictions, VPC
 * Service Controls, SuperQuota, and other policies. - **What has happened:** for each API response,
 * Chemist reports the telemetry data to analytics, auditing, billing, eventing, logging,
 * monitoring, sawmill, and tracing. Chemist also accepts telemetry data not associated with API
 * traffic, such as billing metrics. Example: control: environment: servicecontrol.googleapis.com
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Service Networking API. For a detailed explanation
 * see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class Control extends com.google.api.client.json.GenericJson {

  /**
   * The service controller environment to use. If empty, no control plane feature (like quota and
   * billing) will be enabled. The recommended value for most services is
   * servicecontrol.googleapis.com
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String environment;

  /**
   * The service controller environment to use. If empty, no control plane feature (like quota and
   * billing) will be enabled. The recommended value for most services is
   * servicecontrol.googleapis.com
   * @return value or {@code null} for none
   */
  public java.lang.String getEnvironment() {
    return environment;
  }

  /**
   * The service controller environment to use. If empty, no control plane feature (like quota and
   * billing) will be enabled. The recommended value for most services is
   * servicecontrol.googleapis.com
   * @param environment environment or {@code null} for none
   */
  public Control setEnvironment(java.lang.String environment) {
    this.environment = environment;
    return this;
  }

  @Override
  public Control set(String fieldName, Object value) {
    return (Control) super.set(fieldName, value);
  }

  @Override
  public Control clone() {
    return (Control) super.clone();
  }

}


text:
.yui-overlay {
    position:absolute;
}

.yui-overlay-hidden {
    visibility:hidden
}


text:
(function() {
    var AdminForm = function() {
      // Field converters
      var fieldConverters = [];

      /**
      * Process AJAX fk-widget
      */
      function processAjaxWidget($el, name) {
        var multiple = $el.attr('data-multiple') == '1';

        var opts = {
          width: 'resolve',
          minimumInputLength: 1,
          placeholder: 'data-placeholder',
          ajax: {
            url: $el.attr('data-url'),
            data: function(term, page) {
              return {
                query: term,
                offset: (page - 1) * 10,
                limit: 10
              };
            },
            results: function(data, page) {
              var results = [];

              for (var k in data) {
                var v = data[k];

                results.push({id: v[0], text: v[1]});
              }

              return {
                results: results,
                more: results.length == 10
              };
            }
          },
          initSelection: function(element, callback) {
            $el = $(element);
            var value = jQuery.parseJSON($el.attr('data-json'));
            var result = null;

            if (value) {
              if (multiple) {
                result = [];

                for (var k in value) {
                  var v = value[k];
                  result.push({id: v[0], text: v[1]});
                }

                callback(result);
              } else {
                result = {id: value[0], text: value[1]};
              }
            }

            callback(result);
          }
        };

        if ($el.attr('data-allow-blank'))
          opts['allowClear'] = true;

        opts['multiple'] = multiple;

        $el.select2(opts);
      }

      /**
      * Process data-role attribute for the given input element. Feel free to override
      *
      * @param {Selector} $el jQuery selector
      * @param {String} name data-role value
      */
      this.applyStyle = function($el, name) {
        // Process converters first
        for (var conv in fieldConverters) {
            var fildConv = fieldConverters[conv];

            if (fieldConv($el, name))
                return true;
        }

        switch (name) {
            case 'select2':
                var opts = {
                    width: 'resolve'
                };

                if ($el.attr('data-allow-blank'))
                    opts['allowClear'] = true;

                if ($el.attr('data-tags')) {
                    $.extend(opts, {
                        tokenSeparators: [','],
                        tags: []
                    });
                }

                $el.select2(opts);
                return true;
            case 'select2-ajax':
                processAjaxWidget($el, name);
                return true;
            case 'datepicker':
                $el.datetimepicker({minView: 'month'});
                return true;
            case 'datetimepicker':
                $el.datetimepicker();
                return true;
        }
      };

      /**
      * Add inline form field
      *
      * @method addInlineField
      * @param {String} id Form ID
      * @param {Node} el Form element
      * @param {String} template Form template
      */
      this.addInlineField = function(id, el, template) {
        var $el = $(el);
        var $template = $($(template).text());

        // Figure out new field ID
        var lastField = $el.children('.fa-inline-field').last();

        var prefix = id + '-0';
        if (lastField.length > 0) {
            var parts = $(lastField[0]).attr('id').split('-');
            idx = parseInt(parts[parts.length - 1], 10) + 1;
            prefix = id + '-' + idx;
        }

        // Set form ID
        $template.attr('id', prefix);

        // Fix form IDs
        $('[name]', $template).each(function(e) {
            var me = $(this);

            var id = me.attr('id');
            var name = me.attr('name');

            id = prefix + (id !== '' ? '-' + id : '');
            name = prefix + (name !== '' ? '-' + name : '');

            me.attr('id', id);
            me.attr('name', name);
        });

        $template.appendTo($el);

        // Select first field
        $('input:first', $template).focus();

        // Apply styles
        this.applyGlobalStyles($template);
      };

      /**
      * Apply global input styles.
      *
      * @method applyGlobalStyles
      * @param {Selector} jQuery element
      */
      this.applyGlobalStyles = function(parent) {
        var self = this;

        $('[data-role]', parent).each(function() {
            var $el = $(this);
            self.applyStyle($el, $el.attr('data-role'));
        });
      };
    };

    // Add live event handler
    $('.fa-remove-field').live('click', function(e) {
        e.preventDefault();

        var form = $(this).closest('.fa-inline-field');
        form.remove();
    });

    // Expose faForm globally
    var faForm = window.faForm = new AdminForm();

    // Apply global styles for current page after page loaded
    $(function() {
        faForm.applyGlobalStyles(document);
    });
})();


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace TcxTools
{
    /// <remarks/>
    [Serializable]
    [XmlType(Namespace = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2", TypeName = "Speed_t")]
    public class Speed : Target
    {
        /// <remarks/>
        public Zone SpeedZone { get; set; }
    }
}


text:
namespace std{
    using ::size_t;
} // namespace std
#endif

#ifndef BOOST_NO_CWCHAR

// a couple of libraries which include wchar_t don't include
// wcslen

#if defined(BOOST_DINKUMWARE_STDLIB) && BOOST_DINKUMWARE_STDLIB < 306 \
|| defined(__LIBCOMO__)

namespace std {
inline std::size_t wcslen(const wchar_t * ws)
{
    const wchar_t * eows = ws;
    while(* eows != 0)
        ++eows;
    return eows - ws;
}
} // namespace std

#else

#include <cwchar>
#ifdef BOOST_NO_STDC_NAMESPACE
namespace std{ using ::wcslen; }
#endif

#endif // wcslen

#endif //BOOST_NO_CWCHAR

#endif //BOOST_ARCHIVE_WCSLEN_HPP


text:
<?xml version="1.0" encoding="UTF-8"?>
<!--  Copyright (C) 2013 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
 -->

<resources xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="app_label" msgid="2783841764617238354">"Dokumentumok"</string>
    <string name="title_open" msgid="4353228937663917801">"Megnyitás innen"</string>
    <string name="title_save" msgid="2433679664882857999">"Mentés ide"</string>
    <string name="menu_create_dir" msgid="5947289605844398389">"Mappa létrehozása"</string>
    <string name="menu_grid" msgid="6878021334497835259">"Rács"</string>
    <string name="menu_list" msgid="7279285939892417279">"Lista"</string>
    <string name="menu_sort" msgid="7677740407158414452">"Rendezés"</string>
    <string name="menu_search" msgid="3816712084502856974">"Keresés"</string>
    <string name="menu_settings" msgid="6008033148948428823">"Beállítások"</string>
    <string name="menu_open" msgid="432922957274920903">"Megnyitás"</string>
    <string name="menu_save" msgid="2394743337684426338">"Mentés"</string>
    <string name="menu_share" msgid="3075149983979628146">"Megosztás"</string>
    <string name="menu_delete" msgid="8138799623850614177">"Törlés"</string>
    <string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> kiválasztva"</string>
    <string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
    <string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
    <string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
    <string name="drawer_open" msgid="4545466532430226949">"Gyökérszint megjelenítése"</string>
    <string name="drawer_close" msgid="7602734368552123318">"Gyökérszint elrejtése"</string>
    <string name="save_error" msgid="6167009778003223664">"Nem sikerült menteni a dokumentumot"</string>
    <string name="create_error" msgid="3735649141335444215">"Nem sikerült létrehozni a mappát"</string>
    <string name="query_error" msgid="1222448261663503501">"A dokumentumok lekérése nem sikerült"</string>
    <string name="root_recent" msgid="4470053704320518133">"Legutóbbiak"</string>
    <string name="root_available_bytes" msgid="8568452858617033281">"<xliff:g id="SIZE">%1$s</xliff:g> szabad"</string>
    <string name="root_type_service" msgid="2178854894416775409">"Tárhelyszolgáltatások"</string>
    <string name="root_type_shortcut" msgid="3318760609471618093">"Parancsikonok"</string>
    <string name="root_type_device" msgid="7121342474653483538">"Eszközök"</string>
    <string name="root_type_apps" msgid="8838065367985945189">"További alkalmazások"</string>
    <string name="pref_advanced_devices" msgid="903257239609301276">"Speciális eszközök megjelenítése"</string>
    <string name="pref_file_size" msgid="2826879315743961459">"Fájlméret megjelenítése"</string>
    <string name="pref_device_size" msgid="3542106883278997222">"Eszközméret megjelenítése"</string>
    <string name="empty" msgid="7858882803708117596">"Nincsenek elemek"</string>
    <string name="toast_no_application" msgid="1339885974067891667">"A fájlt nem lehet megnyitni"</string>
    <string name="toast_failed_delete" msgid="2180678019407244069">"Néhány dokumentumot nem lehet törölni"</string>
    <string name="share_via" msgid="8966594246261344259">"Megosztás itt:"</string>
</resources>


text:
package java.util;

/**
 * The Dictionary class is the abstract parent of any
 * class, such as Hashtable, which maps keys to values.
 * Every key and every value is an object. In any one <tt>Dictionary</tt>
 * object, every key is associated with at most one value. Given a
 * <tt>Dictionary</tt> and a key, the associated element can be looked up.
 * Any non-null object can be used as a key and as a value.
 *

 * 字典类基类,键值映射抽象实现类。每一个键最多只对应一个值,不管是键还是值
 * 都不允许出现null。

 * As a rule, the equals method should be used by
 * implementations of this class to decide if two keys are the same.

 * 使用对象的equals方法来判断两个键是否相等。

 *

 * NOTE: This class is obsolete.  New implementations should
 * implement the Map interface, rather than extending this class.

 * 这个类已经被废弃了。新的键值映射类,应该实现Map接口而不是继承本类。
 *
 * @author  unascribed
 * @see     java.util.Map
 * @see     java.lang.Object#equals(java.lang.Object)
 * @see     java.lang.Object#hashCode()
 * @see     java.util.Hashtable
 * @since   JDK1.0
 */
public abstract
class Dictionary<K,V> {
    /**
     * Sole constructor.  (For invocation by subclass constructors, typically
     * implicit.)
     */
    public Dictionary() {
    }

    /**
     * Returns the number of entries (distinct keys) in this dictionary.

     * 该字典中,键值对的个数。

     *
     * @return  the number of keys in this dictionary.
     */
    abstract public int size();

    /**
     * Tests if this dictionary maps no keys to value. The general contract
     * for the <tt>isEmpty</tt> method is that the result is true if and only
     * if this dictionary contains no entries.

     * 判断该字典是否为空,如果一个字典不包含任何键值对,我们认为该字典是空的。

     *
     * @return  true if this dictionary maps no keys to values;
     *          false otherwise.
     */
    abstract public boolean isEmpty();

    /**
     * Returns an enumeration of the keys in this dictionary. The general
     * contract for the keys method is that an <tt>Enumeration</tt> object
     * is returned that will generate all the keys for which this dictionary
     * contains entries.

     * 以Enumeration的形式,返回所有的key。
     *
     * @return  an enumeration of the keys in this dictionary.
     * @see     java.util.Dictionary#elements()
     * @see     java.util.Enumeration
     */
    abstract public Enumeration<K> keys();

    /**
     * Returns an enumeration of the values in this dictionary. The general
     * contract for the <tt>elements</tt> method is that an
     * <tt>Enumeration</tt> is returned that will generate all the elements
     * contained in entries in this dictionary.

     * 以Enumeration的形式,返回所有value。

     *
     * @return  an enumeration of the values in this dictionary.
     * @see     java.util.Dictionary#keys()
     * @see     java.util.Enumeration
     */
    abstract public Enumeration<V> elements();

    /**
     * Returns the value to which the key is mapped in this dictionary.
     * The general contract for the <tt>isEmpty</tt> method is that if this
     * dictionary contains an entry for the specified key, the associated
     * value is returned; otherwise, <tt>null</tt> is returned.

     * 给定Key返回指定的value。如果不存在该键值对,则返回null。如果给定的key
     * 是null,那么抛出NullPointerException异常。

     *
     * @return  the value to which the key is mapped in this dictionary;
     * @param   key   a key in this dictionary.
     *          null if the key is not mapped to any value in
     *          this dictionary.
     * @exception NullPointerException if the <tt>key</tt> is <tt>null</tt>.
     * @see     java.util.Dictionary#put(java.lang.Object, java.lang.Object)
     */
    abstract public V get(Object key);

    /**
     * Maps the specified key to the specified
     * value in this dictionary. Neither the key nor the
     * value can be null.

     * 插入一个键值对,key和value都不能是null。

     *

     * If this dictionary already contains an entry for the specified
     * <tt>key</tt>, the value already in this dictionary for that
     * <tt>key</tt> is returned, after modifying the entry to contain the
     *  new element.
If this dictionary does not already have an entry
     *  for the specified <tt>key</tt>, an entry is created for the
     *  specified <tt>key</tt> and <tt>value</tt>, and <tt>null</tt> is
     *  returned.

     * 如果已经存在一个具有相同key的键值对,那么将该键值对的value修改为指定
     * 的value,并将旧的value返回。

     * 如果不存在具有相同key的键值对,那么返回null。

     *

     * The value can be retrieved by calling the
     * get method with a key that is equal to
     * the original key.
     *
     * @param      key     the hashtable key.
     * @param      value   the value.
     * @return     the previous value to which the key was mapped
     *             in this dictionary, or null if the key did not
     *             have a previous mapping.
     * @exception  NullPointerException  if the key or
     *               value is null.

     * 如果给定的key或者value是null,那么抛出NullPointerException异常。

     * @see        java.lang.Object#equals(java.lang.Object)
     * @see        java.util.Dictionary#get(java.lang.Object)
     */
    abstract public V put(K key, V value);

    /**
     * Removes the key (and its corresponding
     * value) from this dictionary. This method does nothing
     * if the key is not in this dictionary.

     * 根据给定的key找到相应的键值对,并返回对应的value。如果不存在这样的
     * 键值对,那么返回null。

     *
     * @param   key   the key that needs to be removed.
     * @return  the value to which the key had been mapped in this
     *          dictionary, or null if the key did not have a
     *          mapping.
     * @exception NullPointerException if <tt>key</tt> is <tt>null</tt>.

     * 如果给定的key是null,则抛出NullPointerException异常。

     */
    abstract public V remove(Object key);
}


text:
const GoogleSpreadsheet = require('google-spreadsheet')
const Cashbox = require('cashbox')
const deap = require('deap')

module.exports = (app) => {
  var LAST_JOKES = []
  var config = app.config.spreadsheet
  var sheet = new GoogleSpreadsheet(config.id)
  var cache = new Cashbox(deap({
    error: app.log.error.bind(app.log)
  }, app.config.cache))

  return function getJokes (done) {
    cache.get('jokes', (key, callback) => {
      app.log.info('loading jokes from google...')

      sheet.getRows(config.sheet, (err, rows) => {
        if (err) {
          // Treat errors by sending the last good set of jokes
          if (LAST_JOKES.length > 0) {
            app.log.error(err.message)
            return callback(null, LAST_JOKES)
          }

          return callback(err, [])
        }

        // If our data is empty for some reason, send the last good set
        if (!rows || !rows.length) {
          if (LAST_JOKES.length > 0) {
            return callback(null, LAST_JOKES)
          }

          return callback(new Error('no jokes returned'), [])
        }

        var jokes = rows.map((row) => {
          return row.joke
        })

        // Keep a local ref of last good set of jokes
        LAST_JOKES = jokes

        callback(null, jokes)
      })
    }, config.ttl, done)
  }
}


text:
/**
\brief Declaration of the "opentimers" driver.

\author Xavi Vilajosana <xvilajosana@eecs.berkeley.edu>, March 2012.
*/

#ifndef __OPENTIMERS_H
#define __OPENTIMERS_H

#include "openwsn.h"

/**
\addtogroup drivers
\{
\addtogroup OpenTimers
\{
*/

//=========================== define ==========================================

/// Maximum number of timers that can run concurrently
#define MAX_NUM_TIMERS            10

#define MAX_TICKS_IN_SINGLE_CLOCK ((PORT_TIMER_WIDTH)0xFFFFFFFF)

#define TOO_MANY_TIMERS_ERROR     255

#define opentimer_id_t uint8_t

typedef void (*opentimers_cbt)(void);

//=========================== typedef =========================================

typedef enum {
   TIMER_PERIODIC,
   TIMER_ONESHOT,
} timer_type_t;

/*the time can be in tics or in ms*/
typedef enum {
   TIME_MS,
   TIME_TICS,
} time_type_t;

typedef struct {
   uint32_t             period_ticks;       // total number of clock ticks
   PORT_TIMER_WIDTH     ticks_remaining;    // ticks remaining before elapses
   uint16_t             wraps_remaining;    // the clock register is 16 bit, and can't count beyond 32k...
                                            // so period_ticks = wraps_remaining*(32k or uint16_t)
   timer_type_t         type;               // periodic or one-shot
   bool                 isrunning;          // is running?
   opentimers_cbt       callback;           // function to call when elapses
   bool                 hasExpired;         // whether the callback has to be called
} opentimers_t;

//=========================== module variables ================================

typedef struct {
   opentimers_t         timersBuf[MAX_NUM_TIMERS];
   bool                 running;
   PORT_TIMER_WIDTH     currentTimeout; // current timeout, in ticks
} opentimers_vars_t;

//=========================== prototypes ======================================

void           opentimers_init(void);
opentimer_id_t opentimers_start(uint32_t       duration,
                                timer_type_t   type,
                                time_type_t timetype,
                                opentimers_cbt callback);
void           opentimers_setPeriod(opentimer_id_t id,time_type_t timetype, uint32_t       newPeriod);
void           opentimers_stop(opentimer_id_t id);
void           opentimers_restart(opentimer_id_t id);

void           opentimers_sleepTimeCompesation(uint16_t sleepTime);

/**
\}
\}
*/

#endif


text:
<!DOCTYPE html>

  <meta charset="utf-8">
  <title>libdill</title>
  <link rel="stylesheet" type="text/css" href="main.css">

libdill: Structured Concurrency for C

Home

Download

Documentation

Tutorials

FAQ

Tutorial: Implementing a network protocol

Introduction

In this tutorial you will learn how to implement a simple network protocol using libdill.

The source code for individual steps of this tutorial can be found in tutorial/protocol subdirectory.

Step 1: Creating a handle

Handles are libdill's equivalent of file descriptors. A socket implementing our protocol will be pointed to by a handle. Therefore, we have to learn how to create custom handle types.

Standard include file for libdill is libdill.h. In this case, however, we will include libdillimpl.h which defines all the functions libdill.h does but also adds some extra stuff that can be used to implement different plugins to libdill, such as new handle types or new socket types:

#include <libdillimpl.h>

To make it clear what API we are trying to implement, let's start with a simple test program. We'll call our protocol quux and at this point we will just open and close the handle:

int main(void) {
    int h = quux_open();
    assert(h >= 0);
    int rc = hclose(h);
    assert(rc == 0);
    return 0;
}

To start with the implementation we need a structure to hold data for the handle. At the moment it will contain only handle's virtual function table:

struct quux {
    struct hvfs hvfs;
};

Let's add forward declarations for functions that will be filled into the virtual function table. We'll learn what each of them is good for shortly:

static void *quux_hquery(struct hvfs *hvfs, const void *id);
static void quux_hclose(struct hvfs *hvfs);

The quux_open function itself won't do much except for allocating the object, filling in the table of virtual functions and registering it with libdill runtime:

int quux_open(void) {
    int err;
    struct quux *self = malloc(sizeof(struct quux));
    if(!self) {err = ENOMEM; goto error1;}
    self->hvfs.query = quux_hquery;
    self->hvfs.close = quux_hclose;
    int h = hmake(&self->hvfs);
    if(h < 0) {err = errno; goto error2;}
    return h;
error2:
    free(self);
error1:
    errno = err;
    return -1;
}

Function hmake() does the trick. You pass it a virtual function table and it returns a handle. When the standard function like hclose() is called on the handle libdill will forward the call to the corresponding virtual function, in this particular case to quux_hclose(). The interesting part is that the first argument to the virtual function is no longer the handle but rather pointer to the virtual function table. And given that virtual function table is a member of struct quux it's easy to convert it to the pointer to the quux object itself.

quux_hclose() virtual function will deallocate the quux object:

static void quux_hclose(struct hvfs *hvfs) {
    struct quux *self = (struct quux*)hvfs;
    free(self);
}

At the moment we can just return ENOTSUP from the other two virtual functions.

Compile the file and run it to test whether it works as expected:

$ gcc -o step1 step1.c -ldill

Step 2: Implementing an operation on the handle

Consider a UDP socket. It is actually multiple things. It's a handle and as such it exposes functions like hclose(). It's a message socket and as such it exposes functions like msend() and mrecv(). It's also a UDP socket per se and as such it exposes functions like udp_send() or udp_recv().

libdill provides an extremely generic mechanism to address this multi-faceted nature of handles. In fact, the mechanism is so generic that it's almost silly. There's a hquery() function which takes ID as an argument and returns a void pointer. No assumptions are made about the nature of the ID or nature of the returned pointer. Everything is completely opaque.

To see how that can be useful let's implement a new quux function.

First, we have to define an ID for quux object type. Now, this may be a bit confusing, but the ID is actually a void pointer. The advantage of using a pointer as an ID is that if it was an integer you would have to worry about ID collisions, especially if you defined IDs in different libraries that were then linked together. With pointers there's no such problem. You can take a pointer to a global variable and it is guaranteed to be unique as two pieces of data can't live at the same memory location.

static const int quux_type_placeholder = 0;
static const void *quux_type = &quux_type_placeholder;

Second, let's implement hquery() virtual function, empty implementation of which has been created in previous step of this tutorial:

static void *quux_hquery(struct hvfs *hvfs, const void *type) {
    struct quux *self = (struct quux*)hvfs;
    if(type == quux_type) return self;
    errno = ENOTSUP;
    return NULL;
}

To understand what this is good for think of it from user's perspective: You call hquery() function and pass it a handle and the ID of quux handle type. The function will fail with ENOTSUP if the handle is not a quux handle. It will return pointer to quux structure if it is. You can use the returned pointer to perform useful work on the object.

But wait! Doesn't that break encapsulation? Anyone can call hquery() function, get the pointer to raw quux object and mess with it in unforeseen ways.

But no. Note that quux_type is defined as static. The ID is not available anywhere except in the file that implements quux handle. No external code will be able to get the raw object pointer. The encapsulation works as expected after all.

All that being said, we can finally implement our new user-facing function:

int quux_frobnicate(int h) {
    struct quux *self = hquery(h, quux_type);
    if(!self) return -1;
    printf("Kilroy was here!\n");
    return 0;
}

Modify the test to call the new function, compile it and run it:

int main(void) {
    ...
    int rc = quux_frobnicate(h);
    assert(rc == 0);
    ...
}

Step 3: Attaching and detaching a socket

Let's turn the quux handle that we have implemented into quux network socket now.

We won't implement the entire network stack from scratch. The user will layer the quux socket on top of an existing bytestream protocol, such as TCP. The layering will be done via quux_attach() and quux_detach() functions.

In the test program we are going to layer quux protocol on top of ipc protocol (UNIX domain sockets):

coroutine void client(int s) {
    int q = quux_attach(s);
    assert(q >= 0);
    /* Do something useful here! */
    s = quux_detach(q);
    assert(s >= 0);
    int rc = hclose(s);
    assert(rc == 0);
}

int main(void) {
    int ss[2];
    int rc = ipc_pair(ss);
    assert(rc == 0);
    go(client(ss[0]));
    int q = quux_attach(ss[1]);
    assert(q >= 0);
    /* Do something useful here! */
    int s = quux_detach(q);
    assert(s >= 0);
    rc = hclose(s);
    assert(rc == 0);
    return 0;
}

To implement it the socket first need to remember the handle of the underlying protocol so that it can use it when sending and receiving data:

struct quux {
    ...
    int u;
};

To accomodate libdill's naming conventions for protocols running on top of other protocols, we have to rename quux_open() to quux_attach(). Attach function will accept a handle of the underlying bytestream protocol and create quux protocol on top of it:

int quux_attach(int u) {
    int err;
    struct quux *self = malloc(sizeof(struct quux));
    if(!self) {err = ENOMEM; goto error1;}
    self->hvfs.query = quux_hquery;
    self->hvfs.close = quux_hclose;
    self->hvfs.done = quux_hdone;
    self->u = hown(u);
    int h = hmake(&self->hvfs);
    if(h < 0) {int err = errno; goto error2;}
    return h;
error2:
    free(self);
error1:
    errno = err;
    return -1;
}

Note gow hown function is used to take ownership of the underlying socket. hown will give it a new handle number, thus making the old handle number owned by the caller unusable.

We can reuse quux_frobnicate and rename it to quux_detach. It will terminate the quux protocol and return the handle of the underlying protocol:

int quux_detach(int h) {
    struct quux *self = hquery(h, quux_type);
    if(!self) return -1;
    int u = self->u;
    free(self);
    return u;
}

Step 4: Exposing the socket interface

libdill recognizes two kinds of network sockets: bytestream sockets and messages sockets. The main difference between the two is that the latter preserves the message boundaries while the former does not.

Let's say quux will be a message-based protocol. As such, it should expose functions like msend and mrecv to the user.

Do you remember the trick with adding new function to a handle? If not so, re-read step 2 of this tutorial. For adding message socket functions to quux we'll use the same mechanism except that instead of defining our own type ID we will use an existing one (msock_type) defined by libdill. Passing msock_type to hquery function will return pointer to a virtual function table of type msock_vfs. The table is once again defined by libdill:

struct msock_vfs {
    int (*msendl)(struct msock_vfs *vfs,
        struct iolist *first, struct iolist *last, int64_t deadline);
    ssize_t (*mrecvl)(struct msock_vfs *vfs,
        struct iolist *first, struct iolist *last, int64_t deadline);
};

To implement the functions, we have to first add the virtual function table to quux socket object:

struct quux {
    struct hvfs hvfs;
    struct msock_vfs mvfs;
    int u;
};

We'll need forward declarations for msock functions:

static int quux_msendl(struct msock_vfs *mvfs,
    struct iolist *first, struct iolist *last, int64_t deadline);
static ssize_t quux_mrecvl(struct msock_vfs *mvfs,
    struct iolist *first, struct iolist *last, int64_t deadline);

And we have to fill in the virtual function table inside quux_attach() function:

int quux_attach(int u) {
    ...
    self->mvfs.msendl = quux_msendl;
    self->mvfs.mrecvl = quux_mrecvl;
    ...
}

Return the pointer to the msock virtual function table when hquery() is called with msock_type type ID:

static void *quux_hquery(struct hvfs *hvfs, const void *type) {
    struct quux *self = (struct quux*)hvfs;
    if(type == msock_type) return &self->mvfs;
    if(type == quux_type) return self;
    errno = ENOTSUP;
    return NULL;
}

Note that quux_msendl() and quux_mrecvl() receive pointer to msock virtual function table which is not the same as the pointer to quux object. We'll have to convert it. To make that easier let's define this handy macro. It will convert pointer to an embedded structure to a pointer of the enclosing structure:

#define cont(ptr, type, member) \
    ((type*)(((char*) ptr) - offsetof(type, member)))

Using the cont macro we can convert the mvfs into pointer to quux object. Here, for instance, is a stub implementation of quux_msendl() function:

static int quux_msendl(struct msock_vfs *mvfs,
      struct iolist *first, struct iolist *last, int64_t deadline) {
    struct quux *self = cont(mvfs, struct quux, mvfs);
    errno = ENOTSUP;
    return -1;
}

Add a similar stub implementation for quux_mrecvl() function and proceed to the next step.

Step 5: Sending a message

Now that we've spent previous four steps doing boring scaffolding work we can finally do some fun protocol stuff.

Modify the client part of the test program to send a simple text message:

coroutine void client(int s) {
    ...
    int rc = msend(q, "Hello, world!", 13, -1);
    assert(rc == 0);
    ...
}

Function msend will forward the call to our quux_msendl() stub function which, at the moment, does nothing but returns ENOTSUP error. To implement it we have to decide how the quux network protocol will actually look like.

Let's say it will prefix individual messages with 8-bit size. The test message from above is going to look like this on the wire:

Given that size field is a single byte messages can be at most 255 bytes long. That may be a problem in the real world but this is just a tutorial so let's ignore it and move on.

Note that payload data is passed to the quux_msendl() function in form of two pointers to a structure called iolist.

Iolists are libdill's alternative to POSIX iovecs. Where iovecs are arrays of buffers (so called scatter/gather arrays) iolists are linked lists of buffers. Very much like iovec, iolist has iol_base pointer pointing to the data and iol_len field containing the size of the data. Unlike iovec though it has also iol_next field which points to the next buffer in the list. iol_next of the last item in the list is set to NULL.

An iolist instance may look like this:

Don't forget that there's an extra field in iolist, one called iol_rsvd, which should be always set to zero.

Given that we decided to prefix quux messages with message size we have to compute the message size first. We can do so by iterating over the iolist and summing all the buffer sizes. If size is greater than 254 (we'll use number 255 for a special purpose later on) it's an error.

size_t sz = 0;
struct iolist *it;
for(it = first; it; it = it->iol_next)
    sz += it->iol_len;
if(sz > 254) {errno = EMSGSIZE; return -1;}

Now we can send the size and the payload to the underlying socket:

uint8_t c = (uint8_t)sz;
int rc = bsend(self->u, &c, 1, deadline);
if(rc < 0) return -1;
rc = bsendl(self->u, first, last, deadline);
if(rc < 0) return -1;

To indicate success return zero from the function. Then compile and test.

However, let's suppose we want to implement a high-performance protocol. Looking at the code, it's not hard to spot that there's quite a serious performance problem: For each message, the underlying network stack is traversed twice, one for the size byte, second time for the payload. Instead of doing two send calls and traversing the underlying network stack twice we can modify the iolist to include the size byte as well as the payload and send the entire message using a single bsendl() call. The modified iolist may look as follows. Grey parts are the original iolist as passed to quux_msendl() by the user. Black part is the modification done inside quux_msendl():

The code will look like this:

uint8_t c = (uint8_t)sz;
struct iolist hdr = {&c, 1, first, 0};
int rc = bsendl(self->u, &hdr, last, deadline);
if(rc < 0) return -1;

Step 6: Receiving a message

Add the lines to receive a message to the test. Note that mrecvl() function returns size of the message:

int main(void) {
    ...
    char buf[256];
    ssize_t sz = mrecv(q, buf, sizeof(buf), -1);
    assert(sz >= 0);
    printf("%.*s\n", (int)sz, buf);
    ...
}

To implement the receive function we'll have to read the 8-bit size first:

uint8_t sz;
int rc = brecv(self->u, &sz, 1, deadline);
if(rc < 0) return -1;

Great. Now we know that the message is sz bytes long. We are going to read those bytes into user's buffers. But let's take care of one special case first.

libdill allows iolist passed to the receive function to be NULL. What that means is that the user wants to skip a message. This can be easily done given that the underlying protocol provides similar skipping functionality:

if(!first) {
    rc = brecv(self->u, NULL, sz, deadline);
    if(rc < 0) return -1;
    return sz;
}

If the iolist is not NULL we will find out the overall size of the buffer and check whether message will fit into it:

size_t bufsz = 0;
struct iolist *it;
for(it = first; it; it = it->iol_next)
    bufsz += it->iol_len;
if(bufsz < sz) {errno = EMSGSIZE; return -1;}

It the message fits into the buffer payload can be received:

size_t rmn = sz;
for(it = first; it; it = it->iol_next) {
    size_t torecv = rmn < it->iol_len ? rmn : it->iol_len;
    rc = brecv(self->u, it->iol_base, torecv, deadline);
    if(rc < 0) return -1;
    rmn -= torecv;
    if(rmn == 0) break;
}
return sz;

We are facing the same performance problem here as we did in the send function. Calling brecv() multiple times means extra network stack traversals and thus decreased performance. And we can apply a similar solution. We can modify the iolist in such a way that it can be simply forwarded to the underlying socket.

The main problem in this case is that the size of the message may not match the size of the buffer supplied by the user. If message is larger than the buffer we will simply return an error. However, if message is smaller than the buffer there's a problem. Bytestream's brecvl() function has no size argument. It just receives data until the buffer is completely full. Therefore, we will have to shrink the buffer to match the message size.

Let's say the iolist supplied by the user looks like this:

If the message is 5 bytes long we are going to modify the iolist like this. Grey parts are the iolist structures passed to quux socket by the user. Black parts are the modifications:

In iolist-based functions the list is supposed to be unchanged when the function returns. However, it can be temporarily modified while the function is in progress. Therefore, iolists are not guaranteed to be thread- or coroutine-safe.

We are modifying the iolist here, which is all right, but we have to make sure to revert all our changes before the function returns. Note the orig pointer in the above diagram. It won't be passed to the underlying socket. Instead, it will be used to restore the original iolist after sending is done.

The code for the above looks like this:

size_t rmn = sz;
it = first;
while(1) {
    if(it->iol_len >= rmn) break;
    rmn -= it->iol_len;
    it = it->iol_next;
    if(!it) {errno = EMSGSIZE; return -1;}
}
struct iolist orig = *it;
it->iol_len = rmn;
it->iol_next = NULL;
rc = brecvl(self->u, first, last, deadline);
*it = orig;
if(rc < 0) return -1;
return sz;

Compile and run!

Step 7: Error handling

Consider the following scenario: User wants to receive a message. The message is 200 bytes long. However, after reading 100 bytes, receive function times out. That puts you, as the protocol implementor, into an unconfortable position. There's no way to push the 100 bytes that were already received back to the underlying socket. libdill sockets provide no API for that, but even in principle, it would mean that the underlying socket would need an unlimited buffer in case the user wanted to push back one terrabyte of data.

libdill solves this problem by not trying too hard to recover from errors, even from seemingly recoverable ones like ETIMEOUT.

When building on top of a bytestream protocol -- which in unrecoverable by definition -- you thus have to track failures and once error happens return an error for any subsequent attampts to receive a message. And same reasoning and same solution applies to outbound messages.

Note that this does not apply when you are building on top of a message socket. Message sockets may be recoverable. Consider UDP. If receiving one packet fails you can still receive the next packet.

Anyway, to implement error handling in quux protocol, let's add two booleans to the socket to track whether sending/receiving had failed already:

struct quux {
    ...
    int senderr;
    int recverr;
};

Initialize them to false in quux_attach() function:

self->senderr = 0;
self->recverr = 0;

Set senderr to true every time when sending fails and recverr to true every time when receiving fails. For example:

if(sz > 254) {self->senderr = 1; errno = EMSGSIZE; return -1;}

Finally, fail send and receive function if the error flag is set:

static int quux_msendl(struct msock_vfs *mvfs,
      struct iolist *first, struct iolist *last, int64_t deadline) {
    struct quux *self = cont(mvfs, struct quux, mvfs);
    if(self->senderr) {errno = ECONNRESET; return -1;}
    ...
}

static ssize_t quux_mrecvl(struct msock_vfs *mvfs,
      struct iolist *first, struct iolist *last, int64_t deadline) {
    struct quux *self = cont(mvfs, struct quux, mvfs);
    if(self->recverr) {errno = ECONNRESET; return -1;}
    ...
}

Step 8: Initial handshake

Let's say we want to support mutliple versions of quux protocol. When a quux connection is established peers will exchange their version numbers and if those don't match, protocol initialization will fail.

In fact, we don't even need proper handshake. Each peer can simply send its version number and wait for version number from the other party. We'll do this work in quux_attach() function.

Given that sending and receiving are blocking operations quux_attach() will become a blocking operation itself and will accept a deadline parameter:

int quux_attach(int u, int64_t deadline) {
    ...
    const int8_t local_version = 1;
    int rc = bsend(u, &local_version, 1, deadline);
    if(rc < 0) {err = errno; goto error2;}
    uint8_t remote_version;
    rc = brecv(u, &remote_version, 1, deadline);
    if(rc < 0) {err = errno; goto error2;}
    if(remote_version != local_version) {err = EPROTO; goto error2;}
    ...
error2:
    free(self);
error1:
    hclose(u);
    errno = err;
    return -1;
}

Note how failure of initial handshake not only prevents initialization of quux socket, it also closes the underlying socket. This is necessary because otherwise the underlying sockets will be left in undefined state, with just half of quux handshake being done.

Modify the test program accordingly (add deadlines), compile and test.

Step 9: Terminal handshake

Imagine that user wants to close the quux protocol and start a new protocol, say HTTP, on top of the same underlying TCP connection. For that to work both peers would have to make sure that they've received all quux-related data before proceeding. If they had left any unconsumed data in TCP buffers, the subsequent HTTP protocol would read it and get confused by it.

To achieve that the peers will send a single termination byte (255) each to another to mark the end of the stream of quux messages. After doing so they will read any receive and drop all quux messages from the peer until they receive the 255 byte. At that point all the quux data are cleaned up, both peers have consistent view of the world and HTTP protocol can be safely initiated.

Let's start with addinbg two flags to the quux socket object, one meaning "termination byte was already sent", the other "termination byte was already received":

struct quux {
    ...
    int senddone;
    int recvdone;
};

The flags have to be initialized in the quux_attach() function:

int quux_attach(int u, int64_t deadline) {
    ...
    self->senddone = 0;
    self->recvdone = 0;
    ...
}

If termination byte was already sent send function should return EPIPE error:

static int quux_msendl(struct msock_vfs *mvfs,
      struct iolist *first, struct iolist *last, int64_t deadline) {
    ...
    if(self->senddone) {errno = EPIPE; return -1;}
    ...

}

If termination byte was already received receive function should return EPIPE error. Also, we should handle the case when termination byte is received from the peer:

static ssize_t quux_mrecvl(struct msock_vfs *mvfs,
      struct iolist *first, struct iolist *last, int64_t deadline) {
    ...
    if(self->recvdone) {errno = EPIPE; return -1;}
    ...
    if(sz == 255) {self->recvdone = 1; errno = EPIPE; return -1;}
    ...
}

quux_done function will start the terminal handshake. However, it is not supposed to wait till it is finished. The semantics of the done function are "user is not going to send any more data". You can think of it as of EOF marker of a kind.

int quux_done(int h, int64_t deadline) {
    struct quux *self = hquery(h, quux_type);
    if(!self) return -1;
    if(self->senddone) {errno = EPIPE; return -1;}
    if(self->senderr) {errno = ECONNRESET; return -1;}
    uint8_t c = 255;
    int rc = bsend(self->u, &c, 1, deadline);
    if(rc < 0) {self->senderr = 1; return -1;}
    self->senddone = 1;
    return 0;
}

At this point we can modify quux_detach() function so that it properly cleans up any leftover protocol data.

First, it will send the termination byte if it was not already sent. Then it will receive and drop messages until it receives the termination byte:

int quux_detach(int h, int64_t deadline) {
    int err;
    struct quux *self = hquery(h, quux_type);
    if(!self) return -1;
    if(self->senderr || self->recverr) {err = ECONNRESET; goto error;}
    if(!self->senddone) {
        int rc = quux_done(h, deadline);
        if(rc < 0) {err = errno; goto error;}
    }
    while(1) {
        ssize_t sz = quux_mrecvl(&self->mvfs, NULL, NULL, deadline);
        if(sz < 0 && errno == EPIPE) break;
        if(sz < 0) {err = errno; goto error;}
    }
    int u = self->u;
    free(self);
    return u;
error:
    quux_hclose(&self->hvfs);
    errno = err;
    return -1;
}

Note how the socket, including the underlying socket, is closed when the function fails.

Adjust the test, compile and run. You are done with the tutorial. Have fun writing your own network protocols!


text:
package org.eclipse.rdf4j.spring.tx;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @since 4.0.0
 * @author Florian Kleedorfer
 */
@ConfigurationProperties(prefix = "rdf4j.spring.tx")
public class TxProperties {
	private boolean enabled = false;

	public boolean isEnabled() {
		return enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
}