-
-

10.10. Bootstrapper (smart card bootstrap)

+
+

10.10. Bootstrapper and SIM bootstrap

@@ -115,10 +113,19 @@

10.10.1. Appendix H thereof.

-

While communicating with the smart card is considered outside the scope for the -Anjay library, the “bootstrapper” feature, available commercially, implements a -parser for the file format described in section G.3.4 of the Appendix G -mentioned above.

+

The “bootstrapper” feature, available as a commercial extension to the Anjay +library, includes two modules that aid in implementing this part of the +specification:

+
    +
  • bootstrapper implements a parser for the file format described in +section G.3.4 of the Appendix G +mentioned above

  • +
  • sim_bootstrap implements the flow of ISO/IEC 7816-4 commands +necessary to retrieve the aforementioned file

  • +
+

With the above features in place, all that’s left to implement is actual +communication with the smart card, typically sending and receiving AT+CSIM +commands to a cellular modem.

Bootstrapping from smart card has a number of advantages, including:

+
+

10.10.2.2. Enabling and configuring the sim_bootstrap module

+

Similarly, to enable the sim_bootstrap module, you can enable the +ANJAY_WITH_MODULE_SIM_BOOTSTRAP macro in the anjay_config.h file or, if +using CMake, enable the corresponding WITH_MODULE_sim_bootstrap CMake +option. This requires that the bootstrapper feature is also enabled.

+

By default, the module will access the PKCS#15 application directory file and +search it for the EF(DODF-bootstrap) file in a way that is compliant with LwM2M +TS Appendix G mentioned above.

+

However, you can override the OID of the file to look for, by defining the +ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX macro in anjay_config.h +or setting the corresponding MODULE_sim_bootstrap_DATA_OID_OVERRIDE_HEX +CMake option. It shall be set to a string containing hexlified DER +representation of the desired OID. The default, standards-compliant value is +"672b0901" (which corresponds to OID 2.23.43.9.1), but you may need to +change it to a different value, for example some cards are known to use a +mistakenly encoded value of "0604672b0901".

+

Alternatively, you might define the +ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID macro (or set the +MODULE_sim_bootstrap_HARDCODED_FILE_ID CMake option) to bypass the directory +search entirely and set a hardcoded file ID, e.g. 0x6432.

+

Once the module is enabled and configured, you can use the +anjay_sim_bootstrap_stream_create() function to +create an input stream suitable for passing to anjay_bootstrapper(). In the +simplest case, you can also use the anjay_sim_bootstrap_perform() function +that combines both calls and automatically closes the stream as well.

-

10.10.2.2. Bootstrap information generator tool

+

10.10.2.3. Bootstrap information generator tool

The generator.py application, located in the bootstrap directory of Anjay source package, allows generating binary files in the EF LwM2M_Bootstrap format that is supposed to be stored on smart cards, from a human-readable text @@ -246,7 +281,7 @@

10.10.2.2. <

-

10.10.2.3. Example code

+

10.10.2.4. Example code

Note

The full code for the following example can be found in the @@ -255,85 +290,185 @@

10.10.2.3. < commercial version of Anjay that includes the bootstrapper feature.

The example is loosely based on the Installing mandatory Objects -tutorial. However, since the bootstrap information will be loaded from a file, -the setup_security_object() and setup_server_object() functions are no -longer necessary, and the calls to them can be replaced with direct calls to -anjay_security_object_install() and +tutorial, and additionally borrows much of the modem communication code from +Non-IP Data Delivery. Since the bootstrap information will be loaded from a smart +card, the setup_security_object() and setup_server_object() functions +are no longer necessary, and the calls to them can be replaced with direct calls +to anjay_security_object_install() and anjay_server_object_install():

-
int main(int argc, char *argv[]) {
-    if (argc != 3) {
-        avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME BOOTSTRAP_INFO_FILE",
-                argv[0]);
-        return -1;
-    }
+
int main(int argc, char *argv[]) {
+    if (argc != 3) {
+        avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME MODEM_PATH", argv[0]);
+        return -1;
+    }
 
-    const anjay_configuration_t CONFIG = {
-        .endpoint_name = argv[1],
-        .in_buffer_size = 4000,
-        .out_buffer_size = 4000,
-        .msg_cache_size = 4000
-    };
+    const anjay_configuration_t CONFIG = {
+        .endpoint_name = argv[1],
+        .in_buffer_size = 4000,
+        .out_buffer_size = 4000,
+        .msg_cache_size = 4000
+    };
 
-    anjay_t *anjay = anjay_new(&CONFIG);
-    if (!anjay) {
-        avs_log(tutorial, ERROR, "Could not create Anjay object");
-        return -1;
-    }
+    anjay_t *anjay = anjay_new(&CONFIG);
+    if (!anjay) {
+        avs_log(tutorial, ERROR, "Could not create Anjay object");
+        return -1;
+    }
 
-    int result = 0;
+    int result = 0;
     // Setup necessary objects
-    if (anjay_security_object_install(anjay)
-            || anjay_server_object_install(anjay)) {
-        result = -1;
-    }
+    if (anjay_security_object_install(anjay)
+            || anjay_server_object_install(anjay)) {
+        result = -1;
+    }
 
-    if (!result) {
-        result = bootstrap_from_file(anjay, argv[2]);
-    }
-
-    if (!result) {
-        result = anjay_event_loop_run(
-                anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
-    }
+    if (!result) {
+        result = bootstrap_from_sim(anjay, argv[2]);
+    }
+
+    if (!result) {
+        result = anjay_event_loop_run(
+                anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
+    }
 
-    anjay_delete(anjay);
-    return result;
-}
+    anjay_delete(anjay);
+    return result;
+}
 

As you can see, the command line now expects a second argument with a name of the file containing the bootstrap information.

-

This file is loaded using the bootstrap_from_file() function, implemented as +

This file is loaded using the bootstrap_from_sim() function, implemented as follows:

-
static int bootstrap_from_file(anjay_t *anjay, const char *filename) {
-    avs_log(tutorial, INFO, "Attempting to bootstrap from file");
+
typedef struct {
+    avs_buffer_t *buffer;
+} fifo_t;
+
+// ...
 
-    avs_stream_t *file_stream =
-            avs_stream_file_create(filename, AVS_STREAM_FILE_READ);
+typedef struct {
+    fifo_t fifo;
+    int pts_fd;
+} modem_ctx_t;
 
-    if (!file_stream) {
-        avs_log(tutorial, ERROR, "Could not open file");
-        return -1;
-    }
+// ...
 
-    int result = 0;
-    if (avs_is_err(anjay_bootstrapper(anjay, file_stream))) {
-        avs_log(tutorial, ERROR, "Could not bootstrap from file");
-        result = -1;
-    }
+static int sim_perform_command(void *modem_ctx_,
+                               const void *cmd,
+                               size_t cmd_length,
+                               void *out_buf,
+                               size_t out_buf_size,
+                               size_t *out_response_size) {
+    modem_ctx_t *modem_ctx = (modem_ctx_t *) modem_ctx_;
+    char req_buf[REQ_BUF_SIZE];
+    char resp_buf[RESP_BUF_SIZE] = "";
 
-    avs_stream_cleanup(&file_stream);
-    return result;
-}
+    char *req_buf_ptr = req_buf;
+    char *const req_buf_end = req_buf + sizeof(req_buf);
+    int result = avs_simple_snprintf(req_buf_ptr,
+                                     (size_t) (req_buf_end - req_buf_ptr),
+                                     "AT+CSIM=%" PRIu32 ",\"",
+                                     (uint32_t) (2 * cmd_length));
+    if (result < 0) {
+        return result;
+    }
+    req_buf_ptr += result;
+    if ((size_t) (req_buf_end - req_buf_ptr) < 2 * cmd_length) {
+        return -1;
+    }
+    if ((result = avs_hexlify(req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr),
+                              NULL, cmd, cmd_length))) {
+        return result;
+    }
+    req_buf_ptr += 2 * cmd_length;
+    if ((result = avs_simple_snprintf(
+                 req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr), "\"\r\n"))
+            < 0) {
+        return result;
+    }
+    req_buf_ptr += result;
+    ssize_t written =
+            write(modem_ctx->pts_fd, req_buf, (size_t) (req_buf_ptr - req_buf));
+    if (written != (ssize_t) (req_buf_ptr - req_buf)) {
+        return -1;
+    }
+    avs_time_monotonic_t deadline = avs_time_monotonic_add(
+            avs_time_monotonic_now(),
+            avs_time_duration_from_scalar(5, AVS_TIME_S));
+    bool csim_resp_received = false;
+    bool ok_received = false;
+    while (!ok_received) {
+        if (modem_getline(modem_ctx, resp_buf, sizeof(resp_buf), deadline)) {
+            return -1;
+        }
+        const char *resp_terminator = memchr(resp_buf, '\0', sizeof(resp_buf));
+        if (!resp_terminator) {
+            return -1;
+        }
+        if (memcmp(resp_buf, CSIM_RESP, strlen(CSIM_RESP)) == 0) {
+            if (csim_resp_received) {
+                return -1;
+            }
+            errno = 0;
+            char *endptr = NULL;
+            long long resp_reported_length =
+                    strtoll(resp_buf + strlen(CSIM_RESP), &endptr, 10);
+            if (errno || !endptr || endptr[0] != ',' || endptr[1] != '"'
+                    || resp_reported_length < 0
+                    || endptr + resp_reported_length + 2 >= resp_terminator
+                    || endptr[resp_reported_length + 2] != '"'
+                    || avs_unhexlify(out_response_size, (uint8_t *) out_buf,
+                                     out_buf_size, endptr + 2,
+                                     (size_t) resp_reported_length)) {
+                return -1;
+            }
+            csim_resp_received = true;
+        } else if (strcmp(resp_buf, "OK") == 0) {
+            ok_received = true;
+        }
+    }
+    return csim_resp_received ? 0 : -1;
+}
+
+static int bootstrap_from_sim(anjay_t *anjay, const char *modem_device) {
+    modem_ctx_t modem_ctx = {
+        .pts_fd = -1
+    };
+    int result = -1;
+
+    avs_log(tutorial, INFO, "Attempting to bootstrap from SIM card");
+
+    if (fifo_init(&modem_ctx.fifo)) {
+        avs_log(tutorial, ERROR, "could not initialize FIFO");
+        goto finish;
+    }
+    if ((modem_ctx.pts_fd = open(modem_device, O_RDWR)) < 0) {
+        avs_log(tutorial, ERROR, "could not open modem device %s: %s",
+                modem_device, strerror(errno));
+        goto finish;
+    }
+    if (avs_is_err(anjay_sim_bootstrap_perform(anjay, sim_perform_command,
+                                               &modem_ctx))) {
+        avs_log(tutorial, ERROR, "Could not bootstrap from SIM card");
+        goto finish;
+    }
+    result = 0;
+finish:
+    if (modem_ctx.pts_fd >= 0) {
+        close(modem_ctx.pts_fd);
+    }
+    fifo_destroy(&modem_ctx.fifo);
+    return result;
+}
 
-

This shares similarities with the restore_objects_if_possible() function -from the Persistence support tutorial.

-

As mentioned in the Enabling the bootstrapper module section above, to -perform bootstrap using an actual smart card, the -avs_stream_simple_input_create() function from the avs_stream_simple_io.h -header could be used instead of the avs_stream_file_create() call that is -used here to access regular file system.

+

The sim_perform_command() function is a callback that is passed to the +sim_bootstrap module logic, and performs the AT+CSIM command over a +serial port. The modem_getline() function it calls is almost identical to +the one originally implemented for Non-IP Data Delivery.

+

The bootstrap_from_sim() function itself is a wrapper over +anjay_sim_bootstrap_perform() that +additionally initializes and closes the card communication channel.

diff --git a/Compiling_client_applications.html b/Compiling_client_applications.html index f597b1f3..b05dded6 100644 --- a/Compiling_client_applications.html +++ b/Compiling_client_applications.html @@ -4,13 +4,10 @@ - 3. Compiling client applications — Anjay 3.4.0 documentation + 3. Compiling client applications — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -68,8 +65,8 @@
    -
  • - +
  • »
  • +
  • 3. Compiling client applications
@@ -89,7 +86,7 @@

3.1. Compiling the library
cmake . && make
+
cmake . && make
 
@@ -106,7 +103,7 @@

3.2. Cross-compiling

3.2.1. ARM Cortex-M3-powered STM3220

First, prepare a CMake toolchain file (see CMake documentation), then pass CMAKE_TOOLCHAIN_FILE when configuring Anjay:

-
cmake -DCMAKE_TOOLCHAIN_FILE=$YOUR_TOOLCHAIN_FILE . && make
+
cmake -DCMAKE_TOOLCHAIN_FILE=$YOUR_TOOLCHAIN_FILE . && make
 

An example CMake toolchain file for an ARM Cortex-M3-powered STM3220 platform may look like so:

@@ -131,11 +128,11 @@

3.2.2. AndroidCMAKE_TOOLCHAIN_FILE from the NDK (we assume that ANDROID_NDK variable contains a path to the folder where Android NDK is extracted):

-
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-      -DDTLS_BACKEND="" \
-      -DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON \
-      -DANDROID_PLATFORM=android-18 \
-      -DANDROID_ABI=armeabi .
+
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
+      -DDTLS_BACKEND="" \
+      -DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON \
+      -DANDROID_PLATFORM=android-18 \
+      -DANDROID_ABI=armeabi .
 

After that Anjay can be compiled as usual via make.

@@ -166,26 +163,26 @@

3.2.2. Android

Example compilation with mbed TLS backend

First, we compile mbed TLS on Android:

-
$ git clone https://github.com/ARMmbed/mbedtls -b mbedtls-2.5.0
-$ cd mbedtls
-$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-        -DANDROID_PLATFORM=android-18 \
-        -DANDROID_ABI=armeabi \
-        -DENABLE_TESTING=OFF \
-        -DCMAKE_INSTALL_PREFIX=/tmp/mbedtls/install .
-$ make
-$ make install
+
$ git clone https://github.com/ARMmbed/mbedtls -b mbedtls-2.5.0
+$ cd mbedtls
+$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
+        -DANDROID_PLATFORM=android-18 \
+        -DANDROID_ABI=armeabi \
+        -DENABLE_TESTING=OFF \
+        -DCMAKE_INSTALL_PREFIX=/tmp/mbedtls/install .
+$ make
+$ make install
 

We then go back to the Anjay source directory, to reconfigure Anjay to use mbed TLS binaries (we strongly suggest to clean all kind of CMake caches before proceeding, as it may not work otherwise):

-
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-      -DDTLS_BACKEND="mbedtls" \
-      -DMBEDTLS_ROOT_DIR=/tmp/mbedtls/install \
-      -DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON \
-      -DANDROID_PLATFORM=android-18 \
-      -DANDROID_ABI=armeabi .
+
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
+      -DDTLS_BACKEND="mbedtls" \
+      -DMBEDTLS_ROOT_DIR=/tmp/mbedtls/install \
+      -DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON \
+      -DANDROID_PLATFORM=android-18 \
+      -DANDROID_ABI=armeabi .
 

And finally, we run make, finishing the whole procedure.

@@ -198,11 +195,11 @@

3.3. Installing the library3.3.1. Building with CMake

The preferred way of building Anjay is to use CMake.

To install Anjay headers and libraries in /usr/local:

-
cmake . && make && sudo make install
+
cmake . && make && sudo make install
 

A custom installation prefix may be set using CMAKE_INSTALL_PREFIX:

-
@@ -288,7 +285,7 @@

3.4.1. CMake projects3.4.2. Alternative build systems

If Anjay itself has been compiled using CMake, flags necessary for other build systems can be retrieved using cmake command:

-
cmake --find-package -DNAME=anjay -DLANGUAGE=C -DCOMPILER_ID=Generic -DMODE=<mode>
+
cmake --find-package -DNAME=anjay -DLANGUAGE=C -DCOMPILER_ID=Generic -DMODE=<mode>
 

Where <mode> is one of:

@@ -333,7 +330,7 @@

3.4.3. Anjay compiled without CMake

Anjay compiled and installed as shown in the example in the Alternative build systems section

the flags necessary to link client applications would be:

-
-lanjay -lz -lmbedtls -lmbedcrypto -lmbedx509 -lm -pthread
+
-lanjay -lz -lmbedtls -lmbedcrypto -lmbedx509 -lm -pthread
 
diff --git a/FirmwareUpdateTutorial.html b/FirmwareUpdateTutorial.html index 1c2715ae..a76ddb33 100644 --- a/FirmwareUpdateTutorial.html +++ b/FirmwareUpdateTutorial.html @@ -4,13 +4,10 @@ - 6. Firmware Update Tutorial — Anjay 3.4.0 documentation + 6. Firmware Update Tutorial — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,8 +74,8 @@
    -
  • - +
  • »
  • +
  • 6. Firmware Update Tutorial
diff --git a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate.html b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate.html index 023542a5..640d7690 100644 --- a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate.html +++ b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate.html @@ -4,13 +4,10 @@ - 6.7. Advanced Firmware Update — Anjay 3.4.0 documentation + 6.7. Advanced Firmware Update — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -83,9 +80,9 @@
diff --git a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-BasicImplementation.html b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-BasicImplementation.html index 9446b33f..a6a3f696 100644 --- a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-BasicImplementation.html +++ b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-BasicImplementation.html @@ -4,13 +4,10 @@ - 6.7.4. Implementation — Anjay 3.4.0 documentation + 6.7.4. Implementation — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -83,10 +80,10 @@
@@ -128,19 +125,19 @@

6.7.4.1.

6.7.4.2. Advanced Firmware Update API

In order to install the module, we are going to use:

-
int anjay_advanced_fw_update_install(
-        anjay_t *anjay, const anjay_advanced_fw_update_global_config_t *config);
+
int anjay_advanced_fw_update_install(
+        anjay_t *anjay, const anjay_advanced_fw_update_global_config_t *config);
 

With this call we are passing a config that will affect all instances of the Advanced Firmware Update object.

To add an instance of the Advanced Firmware Update object, we are going to use:

-
int anjay_advanced_fw_update_instance_add(
-        anjay_t *anjay,
-        anjay_iid_t iid,
-        const char *component_name,
-        const anjay_advanced_fw_update_handlers_t *handlers,
-        void *user_arg,
-        const anjay_advanced_fw_update_initial_state_t *initial_state);
+
int anjay_advanced_fw_update_instance_add(
+        anjay_t *anjay,
+        anjay_iid_t iid,
+        const char *component_name,
+        const anjay_advanced_fw_update_handlers_t *handlers,
+        void *user_arg,
+        const anjay_advanced_fw_update_initial_state_t *initial_state);
 

The anjay, handlers, user_arg and initial_state arguments are similar @@ -161,28 +158,28 @@

6.7.4.3. component_name argument in anjay_advanced_fw_update_instance_add).

-
#include "advanced_firmware_update.h"
+
#include "advanced_firmware_update.h"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #define AFU_VERSION_STR_MAX_LEN 10
 #define AFU_INSTANCE_NAME_STR_MAX_LEN 10
 #define AFU_FILE_NAME_STR_MAX_LEN 50
 
-typedef struct {
-    anjay_t *anjay;
-    char fw_version[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
-                   [AFU_VERSION_STR_MAX_LEN + 1];
-    char instance_name[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
-                      [AFU_INSTANCE_NAME_STR_MAX_LEN + 1];
-    FILE *new_firmware_file[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
-} advanced_firmware_update_logic_t;
+typedef struct {
+    anjay_t *anjay;
+    char fw_version[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
+                   [AFU_VERSION_STR_MAX_LEN + 1];
+    char instance_name[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
+                      [AFU_INSTANCE_NAME_STR_MAX_LEN + 1];
+    FILE *new_firmware_file[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
+} advanced_firmware_update_logic_t;
 
-static advanced_firmware_update_logic_t afu_logic;
+static advanced_firmware_update_logic_t afu_logic;
 

Number of the firmware instances is defined by AFU_NUMBER_OF_FIRMWARE_INSTANCES. @@ -193,10 +190,10 @@

6.7.4.3.

The implementation of fw_stream_open and fw_update_common_write is quite simple. For each iid we open and write to a separate file.

-
static void get_firmware_download_name(int iid, char *buff) {
-    if (iid == AFU_DEFAULT_FIRMWARE_INSTANCE_IID) {
-        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/firmware_image.bin");
-    } else {
-        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/add_image_%d", iid);
-    }
-}
+
static void get_firmware_download_name(int iid, char *buff) {
+    if (iid == AFU_DEFAULT_FIRMWARE_INSTANCE_IID) {
+        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/firmware_image.bin");
+    } else {
+        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/add_image_%d", iid);
+    }
+}
 
-
static int fw_stream_open(anjay_iid_t iid, void *user_ptr) {
-    (void) user_ptr;
-
-    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-    get_firmware_download_name(iid, file_name);
-
-    if (afu_logic.new_firmware_file[iid]) {
-        avs_log(advance_fu, ERROR, "Already open %s", file_name);
-        return -1;
-    }
-    afu_logic.new_firmware_file[iid] = fopen(file_name, "wb");
-    if (!afu_logic.new_firmware_file[iid]) {
-        avs_log(advance_fu, ERROR, "Could not open %s", file_name);
-        return -1;
-    }
-    return 0;
-}
-
-static int fw_update_common_write(anjay_iid_t iid,
-                                 void *user_ptr,
-                                 const void *data,
-                                 size_t length) {
-    (void) user_ptr;
-
-    if (!afu_logic.new_firmware_file[iid]) {
-        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
-        return -1;
-    }
-    if (length
-            && (fwrite(data, length, 1, afu_logic.new_firmware_file[iid]) != 1
-                || fflush(afu_logic.new_firmware_file[iid]) != 0)) {
-        avs_log(advance_fu, ERROR, "fwrite or fflush failed: %s",
-                strerror(errno));
-        return ANJAY_ADVANCED_FW_UPDATE_ERR_NOT_ENOUGH_SPACE;
-    }
-    return 0;
-}
+
static int fw_stream_open(anjay_iid_t iid, void *user_ptr) {
+    (void) user_ptr;
+
+    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+    get_firmware_download_name(iid, file_name);
+
+    if (afu_logic.new_firmware_file[iid]) {
+        avs_log(advance_fu, ERROR, "Already open %s", file_name);
+        return -1;
+    }
+    afu_logic.new_firmware_file[iid] = fopen(file_name, "wb");
+    if (!afu_logic.new_firmware_file[iid]) {
+        avs_log(advance_fu, ERROR, "Could not open %s", file_name);
+        return -1;
+    }
+    return 0;
+}
+
+static int fw_update_common_write(anjay_iid_t iid,
+                                 void *user_ptr,
+                                 const void *data,
+                                 size_t length) {
+    (void) user_ptr;
+
+    if (!afu_logic.new_firmware_file[iid]) {
+        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
+        return -1;
+    }
+    if (length
+            && (fwrite(data, length, 1, afu_logic.new_firmware_file[iid]) != 1
+                || fflush(afu_logic.new_firmware_file[iid]) != 0)) {
+        avs_log(advance_fu, ERROR, "fwrite or fflush failed: %s",
+                strerror(errno));
+        return ANJAY_ADVANCED_FW_UPDATE_ERR_NOT_ENOUGH_SPACE;
+    }
+    return 0;
+}
 

In fw_update_common_finish after closing the stream, we check the other instances, @@ -275,38 +272,38 @@

6.7.4.3. With anjay_advanced_fw_update_set_linked_instances we set the Linked Instances (/33629/x/15) resource to inform the server that the upgrade will be performed simultaneously on all linked instances.

-
static void add_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
-    const anjay_iid_t *linked_instances;
-    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
-    size_t linked_iids_count = 0;
+
static void add_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
+    const anjay_iid_t *linked_instances;
+    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
+    size_t linked_iids_count = 0;
 
     // get linked instances
-    anjay_advanced_fw_update_get_linked_instances(
-            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
+    anjay_advanced_fw_update_get_linked_instances(
+            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
     // add target_iid to the list
-    for (size_t i = 0; i < linked_iids_count; i++) {
-        linked_target_iids[i] = linked_instances[i];
-    }
-    linked_target_iids[linked_iids_count++] = target_iid;
-    anjay_advanced_fw_update_set_linked_instances(
-            afu_logic.anjay, iid, linked_target_iids, linked_iids_count);
-}
-
-static int fw_update_common_finish(anjay_iid_t iid, void *user_ptr) {
-    (void) user_ptr;
-
-    if (!afu_logic.new_firmware_file[iid]) {
-        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
-        return -1;
-    }
-
-    if (fclose(afu_logic.new_firmware_file[iid])) {
-        avs_log(advance_fu, ERROR, "Closing firmware image failed: object %d",
-                iid);
-        afu_logic.new_firmware_file[iid] = NULL;
-        return -1;
-    }
-    afu_logic.new_firmware_file[iid] = NULL;
+    for (size_t i = 0; i < linked_iids_count; i++) {
+        linked_target_iids[i] = linked_instances[i];
+    }
+    linked_target_iids[linked_iids_count++] = target_iid;
+    anjay_advanced_fw_update_set_linked_instances(
+            afu_logic.anjay, iid, linked_target_iids, linked_iids_count);
+}
+
+static int fw_update_common_finish(anjay_iid_t iid, void *user_ptr) {
+    (void) user_ptr;
+
+    if (!afu_logic.new_firmware_file[iid]) {
+        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
+        return -1;
+    }
+
+    if (fclose(afu_logic.new_firmware_file[iid])) {
+        avs_log(advance_fu, ERROR, "Closing firmware image failed: object %d",
+                iid);
+        afu_logic.new_firmware_file[iid] = NULL;
+        return -1;
+    }
+    afu_logic.new_firmware_file[iid] = NULL;
 
     /*
     If other firmware instances are in downloaded state set linked instances,
@@ -315,20 +312,20 @@ 

6.7.4.3. different and depends on the user's implementation, but always mean that instances will be updated in a batch if the Update resource is executed with no arguments. - */ - for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) { - if (i != iid) { - anjay_advanced_fw_update_state_t state; - anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state); - if (state == ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED) { - add_linked_instance(iid, i); - add_linked_instance(i, iid); - } - } - } - - return 0; -} + */ + for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) { + if (i != iid) { + anjay_advanced_fw_update_state_t state; + anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state); + if (state == ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED) { + add_linked_instance(iid, i); + add_linked_instance(i, iid); + } + } + } + + return 0; +}

@@ -367,344 +364,344 @@

6.7.4.3. refresh_fw_version to update the instance’s firmware versions and we remove all information about all linked and conflicting instances.

-
static void get_add_firmware_file_name(int iid, char *buff) {
-    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "ADD_FILE_%d", iid);
-}
-
-static void get_marker_file_name(int iid, char *buff) {
-    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/fw-updated-marker_%d", iid);
-}
-
-static int move_file(const char *dest, const char *source) {
-    int ret_val = -1;
-    FILE *dest_stream = NULL;
-    FILE *source_stream = fopen(source, "r");
-
-    if (!source_stream) {
-        avs_log(advance_fu, ERROR, "Could not open file: %s", source);
-        goto cleanup;
-    }
-    dest_stream = fopen(dest, "w");
-    if (!dest_stream) {
-        avs_log(advance_fu, ERROR, "Could not open file: %s", dest);
-        fclose(source_stream);
-        goto cleanup;
-    }
-
-    while (!feof(source_stream)) {
-        char buff[1024];
-        size_t bytes_read_1 = fread(buff, 1, sizeof(buff), source_stream);
-        if (fwrite(buff, 1, bytes_read_1, dest_stream) != bytes_read_1) {
-            avs_log(advance_fu, ERROR, "Error during write to file: %s", dest);
-            goto cleanup;
-        }
-    }
-    ret_val = 0;
-
-cleanup:
-    if (dest_stream) {
-        if (fclose(dest_stream)) {
-            avs_log(advance_fu, ERROR, "Could not close file: %s", dest);
-            ret_val = -1;
-        }
-    }
-    if (source_stream) {
-        if (fclose(source_stream)) {
-            avs_log(advance_fu, ERROR, "Could not close file: %s", source);
-            ret_val = -1;
-        }
-    }
-    unlink(source);
-
-    return ret_val;
-}
-
-static void add_conflicting_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
-    const anjay_iid_t *conflicting_instances;
-    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
-    size_t conflicting_iids_count = 0;
+
static void get_add_firmware_file_name(int iid, char *buff) {
+    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "ADD_FILE_%d", iid);
+}
+
+static void get_marker_file_name(int iid, char *buff) {
+    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/fw-updated-marker_%d", iid);
+}
+
+static int move_file(const char *dest, const char *source) {
+    int ret_val = -1;
+    FILE *dest_stream = NULL;
+    FILE *source_stream = fopen(source, "r");
+
+    if (!source_stream) {
+        avs_log(advance_fu, ERROR, "Could not open file: %s", source);
+        goto cleanup;
+    }
+    dest_stream = fopen(dest, "w");
+    if (!dest_stream) {
+        avs_log(advance_fu, ERROR, "Could not open file: %s", dest);
+        fclose(source_stream);
+        goto cleanup;
+    }
+
+    while (!feof(source_stream)) {
+        char buff[1024];
+        size_t bytes_read_1 = fread(buff, 1, sizeof(buff), source_stream);
+        if (fwrite(buff, 1, bytes_read_1, dest_stream) != bytes_read_1) {
+            avs_log(advance_fu, ERROR, "Error during write to file: %s", dest);
+            goto cleanup;
+        }
+    }
+    ret_val = 0;
+
+cleanup:
+    if (dest_stream) {
+        if (fclose(dest_stream)) {
+            avs_log(advance_fu, ERROR, "Could not close file: %s", dest);
+            ret_val = -1;
+        }
+    }
+    if (source_stream) {
+        if (fclose(source_stream)) {
+            avs_log(advance_fu, ERROR, "Could not close file: %s", source);
+            ret_val = -1;
+        }
+    }
+    unlink(source);
+
+    return ret_val;
+}
+
+static void add_conflicting_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
+    const anjay_iid_t *conflicting_instances;
+    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
+    size_t conflicting_iids_count = 0;
 
     // get conflicting instances
-    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
-                                                       &conflicting_instances,
-                                                       &conflicting_iids_count);
+    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
+                                                       &conflicting_instances,
+                                                       &conflicting_iids_count);
     // add target_iid to the list
-    for (size_t i = 0; i < conflicting_iids_count; i++) {
-        conflicting_target_iids[i] = conflicting_instances[i];
-    }
-    conflicting_target_iids[conflicting_iids_count++] = target_iid;
-    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
-                                                       conflicting_target_iids,
-                                                       conflicting_iids_count);
-}
-
-static bool is_update_requested(anjay_iid_t iid,
-                                anjay_iid_t target_iid,
-                                const anjay_iid_t *requested_supplemental_iids,
-                                size_t requested_supplemental_iids_count,
-                                const anjay_iid_t *linked_target_iids,
-                                size_t linked_iids_count) {
-    if (iid == target_iid) {
-        return true;
-    }
-    if (requested_supplemental_iids) {
-        for (size_t i = 0; i < requested_supplemental_iids_count; i++) {
-            if (iid == requested_supplemental_iids[i]) {
-                return true;
-            }
-        }
-    } else if (linked_target_iids) {
-        for (size_t i = 0; i < linked_iids_count; i++) {
-            if (iid == linked_target_iids[i]) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-static char get_firmware_major_version(anjay_iid_t iid, bool is_upgrade) {
-    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-
-    if (is_upgrade == false) {
-        return afu_logic.fw_version[iid][0];
-    }
-
-    get_firmware_download_name(iid, file_name);
+    for (size_t i = 0; i < conflicting_iids_count; i++) {
+        conflicting_target_iids[i] = conflicting_instances[i];
+    }
+    conflicting_target_iids[conflicting_iids_count++] = target_iid;
+    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
+                                                       conflicting_target_iids,
+                                                       conflicting_iids_count);
+}
+
+static bool is_update_requested(anjay_iid_t iid,
+                                anjay_iid_t target_iid,
+                                const anjay_iid_t *requested_supplemental_iids,
+                                size_t requested_supplemental_iids_count,
+                                const anjay_iid_t *linked_target_iids,
+                                size_t linked_iids_count) {
+    if (iid == target_iid) {
+        return true;
+    }
+    if (requested_supplemental_iids) {
+        for (size_t i = 0; i < requested_supplemental_iids_count; i++) {
+            if (iid == requested_supplemental_iids[i]) {
+                return true;
+            }
+        }
+    } else if (linked_target_iids) {
+        for (size_t i = 0; i < linked_iids_count; i++) {
+            if (iid == linked_target_iids[i]) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+static char get_firmware_major_version(anjay_iid_t iid, bool is_upgrade) {
+    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+
+    if (is_upgrade == false) {
+        return afu_logic.fw_version[iid][0];
+    }
+
+    get_firmware_download_name(iid, file_name);
 
     // get value from new file
-    char buff;
-    FILE *stream = fopen(file_name, "r");
-    if (!stream) {
-        avs_log(advance_fu, ERROR, "Could not open file: %s", file_name);
-        return ' ';
-    }
-    if (!fread(&buff, 1, 1, stream)) {
-        avs_log(advance_fu, ERROR, "Could not read from file file: %s",
-                file_name);
-        fclose(stream);
-        return ' ';
-    }
-    if (fclose(stream)) {
-        avs_log(advance_fu, ERROR, "Could not close file: %s", file_name);
-    }
-
-    return buff;
-}
-
-static int refresh_fw_version() {
-    memcpy(afu_logic.fw_version[AFU_DEFAULT_FIRMWARE_INSTANCE_IID],
-        AFU_DEFAULT_FIRMWARE_VERSION, strlen(AFU_DEFAULT_FIRMWARE_VERSION));
-
-    for (int i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        char buff[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-        get_add_firmware_file_name(i, buff);
-        FILE *stream = fopen(buff, "r");
-        if (!stream) {
-            avs_log(advance_fu, ERROR, "Could not open file with iid: %d", i);
-            return -1;
-        }
-        if (!fread(afu_logic.fw_version[i], 1, AFU_VERSION_STR_MAX_LEN,
-                stream)) {
-            avs_log(advance_fu, ERROR, "Could not read file with iid: %d", i);
-            fclose(stream);
-            return -1;
-        }
-        if (fclose(stream)) {
-            avs_log(advance_fu, ERROR, "Could not close file with iid: %d", i);
-            return -1;
-        }
-    }
-
-    for (int i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        avs_log(advance_fu, INFO,
-                "Firmware version for object with IID %d is: %s", i,
-                afu_logic.fw_version[i]);
-    }
-
-    return 0;
-}
+    char buff;
+    FILE *stream = fopen(file_name, "r");
+    if (!stream) {
+        avs_log(advance_fu, ERROR, "Could not open file: %s", file_name);
+        return ' ';
+    }
+    if (!fread(&buff, 1, 1, stream)) {
+        avs_log(advance_fu, ERROR, "Could not read from file file: %s",
+                file_name);
+        fclose(stream);
+        return ' ';
+    }
+    if (fclose(stream)) {
+        avs_log(advance_fu, ERROR, "Could not close file: %s", file_name);
+    }
+
+    return buff;
+}
+
+static int refresh_fw_version() {
+    memcpy(afu_logic.fw_version[AFU_DEFAULT_FIRMWARE_INSTANCE_IID],
+        AFU_DEFAULT_FIRMWARE_VERSION, strlen(AFU_DEFAULT_FIRMWARE_VERSION));
+
+    for (int i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        char buff[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+        get_add_firmware_file_name(i, buff);
+        FILE *stream = fopen(buff, "r");
+        if (!stream) {
+            avs_log(advance_fu, ERROR, "Could not open file with iid: %d", i);
+            return -1;
+        }
+        if (!fread(afu_logic.fw_version[i], 1, AFU_VERSION_STR_MAX_LEN,
+                stream)) {
+            avs_log(advance_fu, ERROR, "Could not read file with iid: %d", i);
+            fclose(stream);
+            return -1;
+        }
+        if (fclose(stream)) {
+            avs_log(advance_fu, ERROR, "Could not close file with iid: %d", i);
+            return -1;
+        }
+    }
+
+    for (int i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        avs_log(advance_fu, INFO,
+                "Firmware version for object with IID %d is: %s", i,
+                afu_logic.fw_version[i]);
+    }
+
+    return 0;
+}
 
-
static int
-fw_update_common_perform_upgrade(anjay_iid_t iid,
-                                 void *user_ptr,
-                                 const anjay_iid_t *requested_supplemental_iids,
-                                 size_t requested_supplemental_iids_count) {
-    (void) user_ptr;
+
static int
+fw_update_common_perform_upgrade(anjay_iid_t iid,
+                                 void *user_ptr,
+                                 const anjay_iid_t *requested_supplemental_iids,
+                                 size_t requested_supplemental_iids_count) {
+    (void) user_ptr;
 
-    const anjay_iid_t *linked_target_iids;
-    bool update_iid[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
-    size_t linked_iids_count = 0;
+    const anjay_iid_t *linked_target_iids;
+    bool update_iid[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
+    size_t linked_iids_count = 0;
 
     // get linked instances
-    anjay_advanced_fw_update_get_linked_instances(
-            afu_logic.anjay, iid, &linked_target_iids, &linked_iids_count);
+    anjay_advanced_fw_update_get_linked_instances(
+            afu_logic.anjay, iid, &linked_target_iids, &linked_iids_count);
 
     /* Prepare list of iid to update. If requested_supplemental_iids is present
-    * use it otherwise use linked_target_iids.*/
-    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (is_update_requested(i, iid, requested_supplemental_iids,
-                                requested_supplemental_iids_count,
-                                linked_target_iids, linked_iids_count)) {
-            update_iid[i] = true;
+    * use it otherwise use linked_target_iids.*/
+    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (is_update_requested(i, iid, requested_supplemental_iids,
+                                requested_supplemental_iids_count,
+                                linked_target_iids, linked_iids_count)) {
+            update_iid[i] = true;
             // check if new file is already downloaded
-            anjay_advanced_fw_update_state_t state;
-            anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state);
-            if ((i != iid)
-                    && (state != ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED)) {
-                avs_log(advance_fu, ERROR,
-                        "Upgrade can't be performed, firmware file with iid %d "
-                        "is not ready",
-                        i);
+            anjay_advanced_fw_update_state_t state;
+            anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state);
+            if ((i != iid)
+                    && (state != ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED)) {
+                avs_log(advance_fu, ERROR,
+                        "Upgrade can't be performed, firmware file with iid %d "
+                        "is not ready",
+                        i);
                 // set conflicting instance
-                add_conflicting_instance(iid, i);
-                return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
-            }
-        } else {
-            update_iid[i] = false;
-        }
-    }
+                add_conflicting_instance(iid, i);
+                return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
+            }
+        } else {
+            update_iid[i] = false;
+        }
+    }
 
     /*
     Check firmware version compatibility.
     In this example major version number is compare - first character in every
     additional image must have the same value. If new file is given (DOWNLOADED
     STATE), get this value from them, otherwise use the old one.
-    */
-    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (update_iid[i] == true) {
-            for (anjay_iid_t j = i + 1; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES;
-                j++) {
-                if (get_firmware_major_version(i, update_iid[i])
-                        != get_firmware_major_version(j, update_iid[j])) {
-                    avs_log(advance_fu, ERROR,
-                            "Upgrade can't be performed, conflicting firmware "
-                            "version between instance %d and %d",
-                            i, j);
+    */
+    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (update_iid[i] == true) {
+            for (anjay_iid_t j = i + 1; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES;
+                j++) {
+                if (get_firmware_major_version(i, update_iid[i])
+                        != get_firmware_major_version(j, update_iid[j])) {
+                    avs_log(advance_fu, ERROR,
+                            "Upgrade can't be performed, conflicting firmware "
+                            "version between instance %d and %d",
+                            i, j);
                     // set conflicting instance due to firmware version
                     // incompatibility
-                    add_conflicting_instance(i, j);
-                    add_conflicting_instance(j, i);
-                    return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
-                }
-            }
-        }
-    }
+                    add_conflicting_instance(i, j);
+                    add_conflicting_instance(j, i);
+                    return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
+                }
+            }
+        }
+    }
 
     /* No errors found, change the status of all requested_supplemental_iids or
-    * linked_target_iids to UPDATING before the actual update process.*/
-    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (update_iid[i] == true) {
-            if (i != iid) {
-                anjay_advanced_fw_update_set_state_and_result(
-                        afu_logic.anjay, i,
-                        ANJAY_ADVANCED_FW_UPDATE_STATE_UPDATING,
-                        ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL);
-            }
-        }
-    }
+    * linked_target_iids to UPDATING before the actual update process.*/
+    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (update_iid[i] == true) {
+            if (i != iid) {
+                anjay_advanced_fw_update_set_state_and_result(
+                        afu_logic.anjay, i,
+                        ANJAY_ADVANCED_FW_UPDATE_STATE_UPDATING,
+                        ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL);
+            }
+        }
+    }
 
     // after firmware versions check, start firmware update, first with
     // additional images
-    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (update_iid[i] == true) {
-            avs_log(advance_fu, INFO, "Perform update on %d instance", i);
-
-            char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-            char current_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-            get_firmware_download_name(i, new_firm_name);
-            get_add_firmware_file_name(i, current_firm_name);
-
-            if (move_file(current_firm_name, new_firm_name)) {
-                avs_log(advance_fu, ERROR,
-                        "Error during the %d additional image swapping", i);
-                return -1;
-            }
+    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (update_iid[i] == true) {
+            avs_log(advance_fu, INFO, "Perform update on %d instance", i);
+
+            char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+            char current_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+            get_firmware_download_name(i, new_firm_name);
+            get_add_firmware_file_name(i, current_firm_name);
+
+            if (move_file(current_firm_name, new_firm_name)) {
+                avs_log(advance_fu, ERROR,
+                        "Error during the %d additional image swapping", i);
+                return -1;
+            }
             // if main application is restarted, set update marker
-            if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
-                char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-                get_marker_file_name(i, marker_name);
-                FILE *marker = fopen(marker_name, "w");
-                if (!marker) {
-                    avs_log(advance_fu, ERROR,
-                            "Marker file could not be created");
-                    return -1;
-                }
-                if (fclose(marker)) {
-                    avs_log(advance_fu, ERROR,
-                            "Marker file could not be close");
-                }
+            if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
+                char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+                get_marker_file_name(i, marker_name);
+                FILE *marker = fopen(marker_name, "w");
+                if (!marker) {
+                    avs_log(advance_fu, ERROR,
+                            "Marker file could not be created");
+                    return -1;
+                }
+                if (fclose(marker)) {
+                    avs_log(advance_fu, ERROR,
+                            "Marker file could not be close");
+                }
             } // if main application is not restarted, update state
-            else {
-                anjay_advanced_fw_update_set_state_and_result(
-                        afu_logic.anjay, i, ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE,
-                        ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS);
-            }
-        }
-    }
+            else {
+                anjay_advanced_fw_update_set_state_and_result(
+                        afu_logic.anjay, i, ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE,
+                        ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS);
+            }
+        }
+    }
 
     // update application
-    if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
-        avs_log(advance_fu, INFO, "Perform update on default instance");
-
-        char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-        char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-        get_firmware_download_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID,
-                                new_firm_name);
-        get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);
-
-        if (chmod(new_firm_name, 0700) == -1) {
-            avs_log(advance_fu, ERROR, "Could not make firmware executable: %s",
-                    strerror(errno));
-            return -1;
-        }
+    if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
+        avs_log(advance_fu, INFO, "Perform update on default instance");
+
+        char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+        char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+        get_firmware_download_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID,
+                                new_firm_name);
+        get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);
+
+        if (chmod(new_firm_name, 0700) == -1) {
+            avs_log(advance_fu, ERROR, "Could not make firmware executable: %s",
+                    strerror(errno));
+            return -1;
+        }
         // Create a marker file, so that the new process knows it is the
         // "upgraded" one
-        FILE *marker = fopen(marker_name, "w");
-        if (!marker) {
-            avs_log(advance_fu, ERROR, "Marker file could not be created");
-            return -1;
-        }
-        if (fclose(marker)) {
-            avs_log(advance_fu, ERROR, "Marker file could not be close");
-        }
+        FILE *marker = fopen(marker_name, "w");
+        if (!marker) {
+            avs_log(advance_fu, ERROR, "Marker file could not be created");
+            return -1;
+        }
+        if (fclose(marker)) {
+            avs_log(advance_fu, ERROR, "Marker file could not be close");
+        }
 
-        assert(ENDPOINT_NAME);
+        assert(ENDPOINT_NAME);
 
         // If the call below succeeds, the firmware is considered as "upgraded",
         // and we hope the newly started client registers to the Server.
-        (void) execl(new_firm_name, new_firm_name, ENDPOINT_NAME, NULL);
-        avs_log(advance_fu, ERROR, "execl() failed: %s", strerror(errno));
+        (void) execl(new_firm_name, new_firm_name, ENDPOINT_NAME, NULL);
+        avs_log(advance_fu, ERROR, "execl() failed: %s", strerror(errno));
         // If we are here, it means execl() failed. Marker file MUST now be
         // removed, as the firmware update failed.
-        unlink(marker_name);
-        return -1;
-    }
+        unlink(marker_name);
+        return -1;
+    }
 
     // update firmware version
-    refresh_fw_version();
+    refresh_fw_version();
 
     // clear conflicting and linked instances in the objects
-    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (update_iid[i] == true) {
-            anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay,
-                                                            i, NULL, 0);
-            anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, i,
-                                                        NULL, 0);
+    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (update_iid[i] == true) {
+            anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay,
+                                                            i, NULL, 0);
+            anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, i,
+                                                        NULL, 0);
             // clear conflicting and linked instances about this object from
             // other objects
-            for (anjay_iid_t j = 0; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES; j++) {
-                if (i != j) {
-                    remove_linked_instance(i, j);
-                    remove_conflicting_instance(i, j);
-                }
-            }
-        }
-    }
-
-    return 0;
-}
+            for (anjay_iid_t j = 0; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES; j++) {
+                if (i != j) {
+                    remove_linked_instance(i, j);
+                    remove_conflicting_instance(i, j);
+                }
+            }
+        }
+    }
+
+    return 0;
+}
 

At the very end we will look at the implementation of fw_update_common_reset. Note that this function will be called @@ -714,81 +711,81 @@

6.7.4.3. fw_update_common_perform_upgrade we do not check the state of each instance, perhaps in your implementation before clearing the linked and conflicting lists, you will need to include logic to check the status of the update.

-
static void remove_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
-    const anjay_iid_t *linked_instances;
-    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
-    size_t linked_iids_count = 0;
-    size_t new_linked_iids_count = 0;
+
static void remove_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
+    const anjay_iid_t *linked_instances;
+    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
+    size_t linked_iids_count = 0;
+    size_t new_linked_iids_count = 0;
 
     // get linked instances
-    anjay_advanced_fw_update_get_linked_instances(
-            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
+    anjay_advanced_fw_update_get_linked_instances(
+            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
     // remove target_iid from the list
-    for (size_t i = 0; i < linked_iids_count; i++) {
-        if (linked_instances[i] != target_iid) {
-            linked_target_iids[new_linked_iids_count++] = linked_instances[i];
-        }
-    }
+    for (size_t i = 0; i < linked_iids_count; i++) {
+        if (linked_instances[i] != target_iid) {
+            linked_target_iids[new_linked_iids_count++] = linked_instances[i];
+        }
+    }
     // update linked list
-    anjay_advanced_fw_update_set_linked_instances(
-            afu_logic.anjay, iid, linked_target_iids, new_linked_iids_count);
-}
+    anjay_advanced_fw_update_set_linked_instances(
+            afu_logic.anjay, iid, linked_target_iids, new_linked_iids_count);
+}
 
-static void remove_conflicting_instance(anjay_iid_t iid,
-                                        anjay_iid_t target_iid) {
-    const anjay_iid_t *conflicting_instances;
-    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
-    size_t conflicting_iids_count = 0;
-    size_t new_conflicting_iids_count = 0;
+static void remove_conflicting_instance(anjay_iid_t iid,
+                                        anjay_iid_t target_iid) {
+    const anjay_iid_t *conflicting_instances;
+    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
+    size_t conflicting_iids_count = 0;
+    size_t new_conflicting_iids_count = 0;
 
     // get conflicting instances
-    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
-                                                    &conflicting_instances,
-                                                    &conflicting_iids_count);
+    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
+                                                    &conflicting_instances,
+                                                    &conflicting_iids_count);
     // remove target_iid from the list
-    for (size_t i = 0; i < conflicting_iids_count; i++) {
-        if (conflicting_instances[i] != target_iid) {
-            conflicting_target_iids[new_conflicting_iids_count++] =
-                    conflicting_instances[i];
-        }
-    }
+    for (size_t i = 0; i < conflicting_iids_count; i++) {
+        if (conflicting_instances[i] != target_iid) {
+            conflicting_target_iids[new_conflicting_iids_count++] =
+                    conflicting_instances[i];
+        }
+    }
     // update conflicting list
-    anjay_advanced_fw_update_set_conflicting_instances(
-            afu_logic.anjay, iid, conflicting_target_iids,
-            new_conflicting_iids_count);
-}
+    anjay_advanced_fw_update_set_conflicting_instances(
+            afu_logic.anjay, iid, conflicting_target_iids,
+            new_conflicting_iids_count);
+}
 
-
void fw_update_common_reset(anjay_iid_t iid, void *user_ptr) {
-    (void) user_ptr;
+
void fw_update_common_reset(anjay_iid_t iid, void *user_ptr) {
+    (void) user_ptr;
 
-    char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-    get_firmware_download_name(iid, new_firm_name);
+    char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+    get_firmware_download_name(iid, new_firm_name);
 
     // Reset can be issued even if the download never started
-    if (afu_logic.new_firmware_file[iid]) {
+    if (afu_logic.new_firmware_file[iid]) {
         // We ignore the result code of fclose(), as fw_reset() can't fail
-        (void) fclose(afu_logic.new_firmware_file[iid]);
+        (void) fclose(afu_logic.new_firmware_file[iid]);
         // and reset our global state to initial value
-        afu_logic.new_firmware_file[iid] = NULL;
-    }
+        afu_logic.new_firmware_file[iid] = NULL;
+    }
     // Finally, let's remove any downloaded payload
-    unlink(new_firm_name);
+    unlink(new_firm_name);
 
     // clear conflicting and linked instances in the object
-    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
-                                                    NULL, 0);
-    anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, iid, NULL,
-                                                0);
+    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
+                                                    NULL, 0);
+    anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, iid, NULL,
+                                                0);
     // clear conflicting and linked instances about this object from other
     // objects
-    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        if (i != iid) {
-            remove_linked_instance(i, iid);
-            remove_conflicting_instance(i, iid);
-        }
-    }
-}
+    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        if (i != iid) {
+            remove_linked_instance(i, iid);
+            remove_conflicting_instance(i, iid);
+        }
+    }
+}
 
@@ -800,110 +797,110 @@

6.7.4.4. AFU_ADD_FILE_DEFAULT_CONTENT). Finally, we call the refresh_fw_version function so that the fw_update_common_get_current_version callback can return the correct value.

-
static const char *fw_update_common_get_current_version(anjay_iid_t iid,
-                                                        void *user_ptr) {
-    (void) user_ptr;
-
-    return (const char *) afu_logic.fw_version[iid];
-}
-
-static const anjay_advanced_fw_update_handlers_t handlers = {
-    .stream_open = fw_stream_open,
-    .stream_write = fw_update_common_write,
-    .stream_finish = fw_update_common_finish,
-    .reset = fw_update_common_reset,
-    .get_current_version = fw_update_common_get_current_version,
-    .perform_upgrade = fw_update_common_perform_upgrade
-};
-
-int afu_update_install(anjay_t *anjay) {
-    anjay_advanced_fw_update_initial_state_t state;
-    char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
-
-    memset(&state, 0, sizeof(state));
-
-    afu_logic.anjay = anjay;
-
-    anjay_advanced_fw_update_global_config_t config = {
-        .prefer_same_socket_downloads = true
-    };
-    int result = anjay_advanced_fw_update_install(anjay, &config);
-    if (result) {
-        avs_log(advance_fu, ERROR,
-                "Could not install advanced firmware object: %d", result);
-        return -1;
-    }
+
static const char *fw_update_common_get_current_version(anjay_iid_t iid,
+                                                        void *user_ptr) {
+    (void) user_ptr;
+
+    return (const char *) afu_logic.fw_version[iid];
+}
+
+static const anjay_advanced_fw_update_handlers_t handlers = {
+    .stream_open = fw_stream_open,
+    .stream_write = fw_update_common_write,
+    .stream_finish = fw_update_common_finish,
+    .reset = fw_update_common_reset,
+    .get_current_version = fw_update_common_get_current_version,
+    .perform_upgrade = fw_update_common_perform_upgrade
+};
+
+int afu_update_install(anjay_t *anjay) {
+    anjay_advanced_fw_update_initial_state_t state;
+    char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
+
+    memset(&state, 0, sizeof(state));
+
+    afu_logic.anjay = anjay;
+
+    anjay_advanced_fw_update_global_config_t config = {
+        .prefer_same_socket_downloads = true
+    };
+    int result = anjay_advanced_fw_update_install(anjay, &config);
+    if (result) {
+        avs_log(advance_fu, ERROR,
+                "Could not install advanced firmware object: %d", result);
+        return -1;
+    }
 
     // check if application was updated
-    get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);
-    if (access(marker_name, F_OK) != -1) {
-        avs_log(advance_fu, INFO, "Application update succeded");
-        state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
-        unlink(marker_name);
-    }
-    result = anjay_advanced_fw_update_instance_add(
-            anjay, AFU_DEFAULT_FIRMWARE_INSTANCE_IID, "application", &handlers,
-            NULL, &state);
-    if (result) {
-        avs_log(advance_fu, ERROR,
-                "Could not add default application instance: %d", result);
-        return -1;
-    }
+    get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);
+    if (access(marker_name, F_OK) != -1) {
+        avs_log(advance_fu, INFO, "Application update succeded");
+        state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
+        unlink(marker_name);
+    }
+    result = anjay_advanced_fw_update_instance_add(
+            anjay, AFU_DEFAULT_FIRMWARE_INSTANCE_IID, "application", &handlers,
+            NULL, &state);
+    if (result) {
+        avs_log(advance_fu, ERROR,
+                "Could not add default application instance: %d", result);
+        return -1;
+    }
 
     // check if additional files were updated, if not create it with default
     // value
-    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
-        memset(marker_name, 0, sizeof(marker_name));
-        get_marker_file_name(i, marker_name);
-        if (access(marker_name, F_OK) != -1) {
-            avs_log(advance_fu, INFO,
-                    "Additional file with idd: %d update succeded", i);
-            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
-            unlink(marker_name);
-        } else {
-            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL;
-        }
-
-        memset(file_name, 0, sizeof(file_name));
-        get_add_firmware_file_name(i, file_name);
+    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
+        memset(marker_name, 0, sizeof(marker_name));
+        get_marker_file_name(i, marker_name);
+        if (access(marker_name, F_OK) != -1) {
+            avs_log(advance_fu, INFO,
+                    "Additional file with idd: %d update succeded", i);
+            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
+            unlink(marker_name);
+        } else {
+            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL;
+        }
+
+        memset(file_name, 0, sizeof(file_name));
+        get_add_firmware_file_name(i, file_name);
         // create file only if not exist
-        if (access(file_name, F_OK) != 0) {
-            FILE *stream = fopen(file_name, "wb");
-            if (!stream) {
-                avs_log(advance_fu, ERROR, "Could not open %s", file_name);
-                return -1;
-            }
-            if (fwrite(AFU_ADD_FILE_DEFAULT_CONTENT,
-                    strlen(AFU_ADD_FILE_DEFAULT_CONTENT), 1, stream)
-                    != 1) {
-                avs_log(advance_fu, ERROR, "Could not write to %s", file_name);
-                fclose(stream);
-                return -1;
-            }
-            if (fclose(stream)) {
-                avs_log(advance_fu, ERROR, "Could not close %s", file_name);
-                return -1;
-            }
-        }
-
-        snprintf(afu_logic.instance_name[i], AFU_INSTANCE_NAME_STR_MAX_LEN,
-                "add_img_%d", i);
-        result = anjay_advanced_fw_update_instance_add(
-                anjay, i, afu_logic.instance_name[i], &handlers, NULL, &state);
-        if (result) {
-            avs_log(advance_fu, ERROR,
-                    "Could not add the additional image instance: %d", result);
-            return -1;
-        }
-    }
-
-    if (refresh_fw_version()) {
-        return -1;
-    }
-
-    return 0;
-}
+        if (access(file_name, F_OK) != 0) {
+            FILE *stream = fopen(file_name, "wb");
+            if (!stream) {
+                avs_log(advance_fu, ERROR, "Could not open %s", file_name);
+                return -1;
+            }
+            if (fwrite(AFU_ADD_FILE_DEFAULT_CONTENT,
+                    strlen(AFU_ADD_FILE_DEFAULT_CONTENT), 1, stream)
+                    != 1) {
+                avs_log(advance_fu, ERROR, "Could not write to %s", file_name);
+                fclose(stream);
+                return -1;
+            }
+            if (fclose(stream)) {
+                avs_log(advance_fu, ERROR, "Could not close %s", file_name);
+                return -1;
+            }
+        }
+
+        snprintf(afu_logic.instance_name[i], AFU_INSTANCE_NAME_STR_MAX_LEN,
+                "add_img_%d", i);
+        result = anjay_advanced_fw_update_instance_add(
+                anjay, i, afu_logic.instance_name[i], &handlers, NULL, &state);
+        if (result) {
+            avs_log(advance_fu, ERROR,
+                    "Could not add the additional image instance: %d", result);
+            return -1;
+        }
+    }
+
+    if (refresh_fw_version()) {
+        return -1;
+    }
+
+    return 0;
+}
 
diff --git a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-Examples.html b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-Examples.html index 37e4639e..a0ae3a7f 100644 --- a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-Examples.html +++ b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-Examples.html @@ -4,13 +4,10 @@ - 6.7.3. Examples — Anjay 3.4.0 documentation + 6.7.3. Examples — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -83,10 +80,10 @@
diff --git a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-ResourceDefinitions.html b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-ResourceDefinitions.html index d1aeada9..10a903d6 100644 --- a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-ResourceDefinitions.html +++ b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-ResourceDefinitions.html @@ -4,13 +4,10 @@ - 6.7.1. Resource definitions — Anjay 3.4.0 documentation + 6.7.1. Resource definitions — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -83,10 +80,10 @@
diff --git a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-StateDiagram.html b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-StateDiagram.html index 8cf9bcb4..80132bf2 100644 --- a/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-StateDiagram.html +++ b/FirmwareUpdateTutorial/FU-AdvancedFirmwareUpdate/FU-AFU-StateDiagram.html @@ -4,13 +4,10 @@ - 6.7.2. Firmware Update State Diagram — Anjay 3.4.0 documentation + 6.7.2. Firmware Update State Diagram — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -83,10 +80,10 @@
diff --git a/FirmwareUpdateTutorial/FU-BasicImplementation.html b/FirmwareUpdateTutorial/FU-BasicImplementation.html index 5aa096ef..57ebc309 100644 --- a/FirmwareUpdateTutorial/FU-BasicImplementation.html +++ b/FirmwareUpdateTutorial/FU-BasicImplementation.html @@ -4,13 +4,10 @@ - 6.2. Basic implementation — Anjay 3.4.0 documentation + 6.2. Basic implementation — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
@@ -110,11 +107,11 @@

6.2.1. Project structure

6.2.2. Installing the Firmware Update module

In order to install the module, we are going to use:

-
int anjay_fw_update_install(
-        anjay_t *anjay,
-        const anjay_fw_update_handlers_t *handlers,
-        void *user_arg,
-        const anjay_fw_update_initial_state_t *initial_state);
+
int anjay_fw_update_install(
+        anjay_t *anjay,
+        const anjay_fw_update_handlers_t *handlers,
+        void *user_arg,
+        const anjay_fw_update_initial_state_t *initial_state);
 

The important arguments for us at this point are anjay, handlers @@ -132,167 +129,167 @@

6.2.1. Project structurefirmware_update.h:

#ifndef FIRMWARE_UPDATE_H
 #define FIRMWARE_UPDATE_H
-#include <anjay/anjay.h>
-#include <anjay/fw_update.h>
+#include <anjay/anjay.h>
+#include <anjay/fw_update.h>
 
 /**
  * Buffer for the endpoint name that will be used when re-launching the client
  * after firmware upgrade.
- */
-extern const char *ENDPOINT_NAME;
+ */
+extern const char *ENDPOINT_NAME;
 
 /**
  * Installs the firmware update module.
  *
  * @returns 0 on success, negative value otherwise.
- */
-int fw_update_install(anjay_t *anjay);
+ */
+int fw_update_install(anjay_t *anjay);
 
 #endif // FIRMWARE_UPDATE_H
 

We invoke it in main.c by performing two (highlighted) modifications:

-
#include <anjay/anjay.h>
-#include <anjay/security.h>
-#include <anjay/server.h>
-#include <avsystem/commons/avs_log.h>
+
#include <anjay/anjay.h>
+#include <anjay/security.h>
+#include <anjay/server.h>
+#include <avsystem/commons/avs_log.h>
 
-#include "firmware_update.h"
-#include "time_object.h"
+#include "firmware_update.h"
+#include "time_object.h"
 
-typedef struct {
-    anjay_t *anjay;
-    const anjay_dm_object_def_t **time_object;
-} notify_job_args_t;
+typedef struct {
+    anjay_t *anjay;
+    const anjay_dm_object_def_t **time_object;
+} notify_job_args_t;
 
 // Periodically notifies the library about Resource value changes
-static void notify_job(avs_sched_t *sched, const void *args_ptr) {
-    const notify_job_args_t *args = (const notify_job_args_t *) args_ptr;
+static void notify_job(avs_sched_t *sched, const void *args_ptr) {
+    const notify_job_args_t *args = (const notify_job_args_t *) args_ptr;
 
-    time_object_notify(args->anjay, args->time_object);
+    time_object_notify(args->anjay, args->time_object);
 
     // Schedule run of the same function after 1 second
-    AVS_SCHED_DELAYED(sched, NULL, avs_time_duration_from_scalar(1, AVS_TIME_S),
-                      notify_job, args, sizeof(*args));
-}
+    AVS_SCHED_DELAYED(sched, NULL, avs_time_duration_from_scalar(1, AVS_TIME_S),
+                      notify_job, args, sizeof(*args));
+}
 
 // Installs Security Object and adds and instance of it.
 // An instance of Security Object provides information needed to connect to
 // LwM2M server.
-static int setup_security_object(anjay_t *anjay) {
-    if (anjay_security_object_install(anjay)) {
-        return -1;
-    }
-
-    static const char PSK_IDENTITY[] = "identity";
-    static const char PSK_KEY[] = "P4s$w0rd";
-
-    anjay_security_instance_t security_instance = {
-        .ssid = 1,
-        .server_uri = "coaps://eu.iot.avsystem.cloud:5684",
-        .security_mode = ANJAY_SECURITY_PSK,
-        .public_cert_or_psk_identity = (const uint8_t *) PSK_IDENTITY,
-        .public_cert_or_psk_identity_size = strlen(PSK_IDENTITY),
-        .private_cert_or_psk_key = (const uint8_t *) PSK_KEY,
-        .private_cert_or_psk_key_size = strlen(PSK_KEY)
-    };
+static int setup_security_object(anjay_t *anjay) {
+    if (anjay_security_object_install(anjay)) {
+        return -1;
+    }
+
+    static const char PSK_IDENTITY[] = "identity";
+    static const char PSK_KEY[] = "P4s$w0rd";
+
+    anjay_security_instance_t security_instance = {
+        .ssid = 1,
+        .server_uri = "coaps://eu.iot.avsystem.cloud:5684",
+        .security_mode = ANJAY_SECURITY_PSK,
+        .public_cert_or_psk_identity = (const uint8_t *) PSK_IDENTITY,
+        .public_cert_or_psk_identity_size = strlen(PSK_IDENTITY),
+        .private_cert_or_psk_key = (const uint8_t *) PSK_KEY,
+        .private_cert_or_psk_key_size = strlen(PSK_KEY)
+    };
 
     // Anjay will assign Instance ID automatically
-    anjay_iid_t security_instance_id = ANJAY_ID_INVALID;
-    if (anjay_security_object_add_instance(anjay, &security_instance,
-                                           &security_instance_id)) {
-        return -1;
-    }
+    anjay_iid_t security_instance_id = ANJAY_ID_INVALID;
+    if (anjay_security_object_add_instance(anjay, &security_instance,
+                                           &security_instance_id)) {
+        return -1;
+    }
 
-    return 0;
-}
+    return 0;
+}
 
 // Installs Server Object and adds and instance of it.
 // An instance of Server Object provides the data related to a LwM2M Server.
-static int setup_server_object(anjay_t *anjay) {
-    if (anjay_server_object_install(anjay)) {
-        return -1;
-    }
+static int setup_server_object(anjay_t *anjay) {
+    if (anjay_server_object_install(anjay)) {
+        return -1;
+    }
 
-    const anjay_server_instance_t server_instance = {
+    const anjay_server_instance_t server_instance = {
         // Server Short ID
-        .ssid = 1,
+        .ssid = 1,
         // Client will send Update message often than every 60 seconds
-        .lifetime = 60,
+        .lifetime = 60,
         // Disable Default Minimum Period resource
-        .default_min_period = -1,
+        .default_min_period = -1,
         // Disable Default Maximum Period resource
-        .default_max_period = -1,
+        .default_max_period = -1,
         // Disable Disable Timeout resource
-        .disable_timeout = -1,
+        .disable_timeout = -1,
         // Sets preferred transport to UDP
-        .binding = "U"
-    };
+        .binding = "U"
+    };
 
     // Anjay will assign Instance ID automatically
-    anjay_iid_t server_instance_id = ANJAY_ID_INVALID;
-    if (anjay_server_object_add_instance(anjay, &server_instance,
-                                         &server_instance_id)) {
-        return -1;
-    }
-
-    return 0;
-}
-
-int main(int argc, char *argv[]) {
-    if (argc != 2) {
-        avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME", argv[0]);
-        return -1;
-    }
-
-    ENDPOINT_NAME = argv[1];
-
-    const anjay_configuration_t CONFIG = {
-        .endpoint_name = ENDPOINT_NAME,
-        .in_buffer_size = 4000,
-        .out_buffer_size = 4000,
-        .msg_cache_size = 4000
-    };
-
-    anjay_t *anjay = anjay_new(&CONFIG);
-    if (!anjay) {
-        avs_log(tutorial, ERROR, "Could not create Anjay object");
-        return -1;
-    }
-
-    int result = 0;
+    anjay_iid_t server_instance_id = ANJAY_ID_INVALID;
+    if (anjay_server_object_add_instance(anjay, &server_instance,
+                                         &server_instance_id)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char *argv[]) {
+    if (argc != 2) {
+        avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME", argv[0]);
+        return -1;
+    }
+
+    ENDPOINT_NAME = argv[1];
+
+    const anjay_configuration_t CONFIG = {
+        .endpoint_name = ENDPOINT_NAME,
+        .in_buffer_size = 4000,
+        .out_buffer_size = 4000,
+        .msg_cache_size = 4000
+    };
+
+    anjay_t *anjay = anjay_new(&CONFIG);
+    if (!anjay) {
+        avs_log(tutorial, ERROR, "Could not create Anjay object");
+        return -1;
+    }
+
+    int result = 0;
     // Setup necessary objects
-    if (setup_security_object(anjay) || setup_server_object(anjay)
-            || fw_update_install(anjay)) {
-        result = -1;
-    }
-
-    const anjay_dm_object_def_t **time_object = NULL;
-    if (!result) {
-        time_object = time_object_create();
-        if (time_object) {
-            result = anjay_register_object(anjay, time_object);
-        } else {
-            result = -1;
-        }
-    }
-
-    if (!result) {
+    if (setup_security_object(anjay) || setup_server_object(anjay)
+            || fw_update_install(anjay)) {
+        result = -1;
+    }
+
+    const anjay_dm_object_def_t **time_object = NULL;
+    if (!result) {
+        time_object = time_object_create();
+        if (time_object) {
+            result = anjay_register_object(anjay, time_object);
+        } else {
+            result = -1;
+        }
+    }
+
+    if (!result) {
         // Run notify_job the first time;
         // this will schedule periodic calls to itself via the scheduler
-        notify_job(anjay_get_scheduler(anjay), &(const notify_job_args_t) {
-                                                   .anjay = anjay,
-                                                   .time_object = time_object
-                                               });
-
-        result = anjay_event_loop_run(
-                anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
-    }
-
-    anjay_delete(anjay);
-    time_object_release(time_object);
-    return result;
-}
+        notify_job(anjay_get_scheduler(anjay), &(const notify_job_args_t) {
+                                                   .anjay = anjay,
+                                                   .time_object = time_object
+                                               });
+
+        result = anjay_event_loop_run(
+                anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
+    }
+
+    anjay_delete(anjay);
+    time_object_release(time_object);
+    return result;
+}
 
@@ -315,15 +312,15 @@

6.2.3. Implementing handlers and install of these?

We can use a globally allocated structure and pack entire shared state into it. In firmware_update.c it looks like this:

-
#include "./firmware_update.h"
+
#include "./firmware_update.h"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
-static struct fw_state_t { FILE *firmware_file; } FW_STATE;
+static struct fw_state_t { FILE *firmware_file; } FW_STATE;
 
@@ -334,70 +331,70 @@

6.2.3. Implementing handlers and install

Having the global state structure, we can proceed with implementation of: fw_stream_open, fw_stream_write and fw_stream_finish, keeping in mind our brief discussion at the beginning of the section:

-
static struct fw_state_t { FILE *firmware_file; } FW_STATE;
+
static struct fw_state_t { FILE *firmware_file; } FW_STATE;
 
-static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
+static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
 
-static int fw_stream_open(void *user_ptr,
-                          const char *package_uri,
-                          const struct anjay_etag *package_etag) {
+static int fw_stream_open(void *user_ptr,
+                          const char *package_uri,
+                          const struct anjay_etag *package_etag) {
     // For a moment, we don't need to care about any of the arguments passed.
-    (void) user_ptr;
-    (void) package_uri;
-    (void) package_etag;
+    (void) user_ptr;
+    (void) package_uri;
+    (void) package_etag;
 
     // It's worth ensuring we start with a NULL firmware_file. In the end
     // it would be our responsibility to manage this pointer, and we want
     // to make sure we never leak any memory.
-    assert(FW_STATE.firmware_file == NULL);
+    assert(FW_STATE.firmware_file == NULL);
     // We're about to create a firmware file for writing
-    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
-    if (!FW_STATE.firmware_file) {
-        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
-        return -1;
-    }
+    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
+    if (!FW_STATE.firmware_file) {
+        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
+        return -1;
+    }
     // We've succeeded
-    return 0;
-}
+    return 0;
+}
 
-static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
-    (void) user_ptr;
+static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
+    (void) user_ptr;
     // We only need to write to file and check if that succeeded
-    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1) {
-        fprintf(stderr, "Writing to firmware image failed\n");
-        return -1;
-    }
-    return 0;
-}
+    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1) {
+        fprintf(stderr, "Writing to firmware image failed\n");
+        return -1;
+    }
+    return 0;
+}
 
-static int fw_stream_finish(void *user_ptr) {
-    (void) user_ptr;
-    assert(FW_STATE.firmware_file != NULL);
+static int fw_stream_finish(void *user_ptr) {
+    (void) user_ptr;
+    assert(FW_STATE.firmware_file != NULL);
 
-    if (fclose(FW_STATE.firmware_file)) {
-        fprintf(stderr, "Closing firmware image failed\n");
-        FW_STATE.firmware_file = NULL;
-        return -1;
-    }
-    FW_STATE.firmware_file = NULL;
-    return 0;
-}
+    if (fclose(FW_STATE.firmware_file)) {
+        fprintf(stderr, "Closing firmware image failed\n");
+        FW_STATE.firmware_file = NULL;
+        return -1;
+    }
+    FW_STATE.firmware_file = NULL;
+    return 0;
+}
 

Next in queue is fw_reset, which is called when something on the Client or the Server side goes wrong, or if the Server decides to not perform firmware update. We can implement it as follows:

-
static void fw_reset(void *user_ptr) {
+
static void fw_reset(void *user_ptr) {
     // Reset can be issued even if the download never started.
-    if (FW_STATE.firmware_file) {
+    if (FW_STATE.firmware_file) {
         // We ignore the result code of fclose(), as fw_reset() can't fail.
-        (void) fclose(FW_STATE.firmware_file);
+        (void) fclose(FW_STATE.firmware_file);
         // and reset our global state to initial value.
-        FW_STATE.firmware_file = NULL;
-    }
+        FW_STATE.firmware_file = NULL;
+    }
     // Finally, let's remove any downloaded payload
-    unlink(FW_IMAGE_DOWNLOAD_NAME);
-}
+    unlink(FW_IMAGE_DOWNLOAD_NAME);
+}
 

And finally, fw_perform_upgrade as well as fw_update_install are to @@ -435,59 +432,59 @@

6.2.3. Implementing handlers and install

The code is self explanatory:

// A part of a rather simple logic checking if the firmware update was
 // successfully performed.
-static const char *FW_UPDATED_MARKER = "/tmp/fw-updated-marker";
-
-static int fw_perform_upgrade(void *user_ptr) {
-    if (chmod(FW_IMAGE_DOWNLOAD_NAME, 0700) == -1) {
-        fprintf(stderr,
-                "Could not make firmware executable: %s\n",
-                strerror(errno));
-        return -1;
-    }
+static const char *FW_UPDATED_MARKER = "/tmp/fw-updated-marker";
+
+static int fw_perform_upgrade(void *user_ptr) {
+    if (chmod(FW_IMAGE_DOWNLOAD_NAME, 0700) == -1) {
+        fprintf(stderr,
+                "Could not make firmware executable: %s\n",
+                strerror(errno));
+        return -1;
+    }
     // Create a marker file, so that the new process knows it is the "upgraded"
     // one
-    FILE *marker = fopen(FW_UPDATED_MARKER, "w");
-    if (!marker) {
-        fprintf(stderr, "Marker file could not be created\n");
-        return -1;
-    }
-    fclose(marker);
-
-    assert(ENDPOINT_NAME);
+    FILE *marker = fopen(FW_UPDATED_MARKER, "w");
+    if (!marker) {
+        fprintf(stderr, "Marker file could not be created\n");
+        return -1;
+    }
+    fclose(marker);
+
+    assert(ENDPOINT_NAME);
     // If the call below succeeds, the firmware is considered as "upgraded",
     // and we hope the newly started client registers to the Server.
-    (void) execl(FW_IMAGE_DOWNLOAD_NAME, FW_IMAGE_DOWNLOAD_NAME, ENDPOINT_NAME,
-                 NULL);
-    fprintf(stderr, "execl() failed: %s\n", strerror(errno));
+    (void) execl(FW_IMAGE_DOWNLOAD_NAME, FW_IMAGE_DOWNLOAD_NAME, ENDPOINT_NAME,
+                 NULL);
+    fprintf(stderr, "execl() failed: %s\n", strerror(errno));
     // If we are here, it means execl() failed. Marker file MUST now be removed,
     // as the firmware update failed.
-    unlink(FW_UPDATED_MARKER);
-    return -1;
-}
+    unlink(FW_UPDATED_MARKER);
+    return -1;
+}
 
-static const anjay_fw_update_handlers_t HANDLERS = {
-    .stream_open = fw_stream_open,
-    .stream_write = fw_stream_write,
-    .stream_finish = fw_stream_finish,
-    .reset = fw_reset,
-    .perform_upgrade = fw_perform_upgrade
-};
+static const anjay_fw_update_handlers_t HANDLERS = {
+    .stream_open = fw_stream_open,
+    .stream_write = fw_stream_write,
+    .stream_finish = fw_stream_finish,
+    .reset = fw_reset,
+    .perform_upgrade = fw_perform_upgrade
+};
 
-const char *ENDPOINT_NAME = NULL;
+const char *ENDPOINT_NAME = NULL;
 
-int fw_update_install(anjay_t *anjay) {
-    anjay_fw_update_initial_state_t state;
-    memset(&state, 0, sizeof(state));
+int fw_update_install(anjay_t *anjay) {
+    anjay_fw_update_initial_state_t state;
+    memset(&state, 0, sizeof(state));
 
-    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
+    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
         // marker file exists, it means firmware update succeded!
-        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
-        unlink(FW_UPDATED_MARKER);
-    }
+        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
+        unlink(FW_UPDATED_MARKER);
+    }
     // install the module, pass handlers that we implemented and initial state
     // that we discovered upon startup
-    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
-}
+    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
+}
 
diff --git a/FirmwareUpdateTutorial/FU-DownloadResumption.html b/FirmwareUpdateTutorial/FU-DownloadResumption.html index 8ef51c6e..167fa319 100644 --- a/FirmwareUpdateTutorial/FU-DownloadResumption.html +++ b/FirmwareUpdateTutorial/FU-DownloadResumption.html @@ -4,13 +4,10 @@ - 6.6. Download resumption — Anjay 3.4.0 documentation + 6.6. Download resumption — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
@@ -104,13 +101,13 @@

6.6.1. Introduction

6.6.2. Anjay and Firmware Update initial state

Let’s have a look at the anjay_fw_update_initial_state_t:

-
typedef struct {
+
typedef struct {
     /**
      * Controls initialization of the State and Update Result resources. It is
      * intended to be used after a reboot caused by a firmware update attempt,
      * to report the update result.
-     */
-    anjay_fw_update_initial_result_t result;
+     */
+    anjay_fw_update_initial_result_t result;
 
     /**
      * Value to initialize the Package URI resource with. The passed string is
@@ -127,8 +124,8 @@ 

6.6.2. Anjay and Firmware Update initial * mechanism. * * In all other cases it is ignored. - */ - const char *persisted_uri; + */ + const char *persisted_uri; /** * Number of bytes that has been already successfully downloaded and are @@ -140,8 +137,8 @@

6.6.2. Anjay and Firmware Update initial * passed chunk of data at the offset set here. If resumption from the set * offset is impossible, the library will call @ref anjay_fw_update_reset_t * and @ref anjay_fw_update_stream_open_t to restart the download process. - */ - size_t resume_offset; + */ + size_t resume_offset; /** * ETag of the download process to resume. The passed value is copied, so @@ -153,26 +150,26 @@

6.6.2. Anjay and Firmware Update initial * case, @ref anjay_fw_update_reset_t handler will be called from * @ref anjay_fw_update_install to reset the Firmware Update object into the * Idle state. - */ - const struct anjay_etag *resume_etag; + */ + const struct anjay_etag *resume_etag; /** * Informs the module to try reusing sockets of existing LwM2M Servers to * download the firmware image if the download URI matches any of the LwM2M * Servers. - */ - bool prefer_same_socket_downloads; + */ + bool prefer_same_socket_downloads; #ifdef ANJAY_WITH_SEND /** * Enables using LwM2M Send to report State, Update Result and Firmware * Version to the LwM2M Server (if LwM2M Send is enabled) during firmware * update. - */ - bool use_lwm2m_send; + */ + bool use_lwm2m_send; #endif // ANJAY_WITH_SEND -} anjay_fw_update_initial_state_t; +} anjay_fw_update_initial_state_t;

The highlighted fields can be used to arrange a download resumption. Recall @@ -199,133 +196,133 @@

6.6.2. Anjay and Firmware Update initial the download state as well as utility functions that’d store and restore the state from persistent storage:

#define _DEFAULT_SOURCE // for fileno()
-#include "./firmware_update.h"
+#include "./firmware_update.h"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
-typedef struct {
-    char *persisted_uri;
-    uint32_t resume_offset;
-    anjay_etag_t *resume_etag;
-} download_state_t;
+typedef struct {
+    char *persisted_uri;
+    uint32_t resume_offset;
+    anjay_etag_t *resume_etag;
+} download_state_t;
 
-static const char *FW_DOWNLOAD_STATE_NAME = "firmware_dl_state.bin";
+static const char *FW_DOWNLOAD_STATE_NAME = "firmware_dl_state.bin";
 
-static int store_etag(FILE *fp, const anjay_etag_t *etag) {
-    assert(etag);
-    if (fwrite(&etag->size, sizeof(etag->size), 1, fp) != 1) {
-        return -1;
-    }
-    if (etag->size > 0 && fwrite(etag->value, etag->size, 1, fp) != 1) {
-        return -1;
-    }
-    return 0;
-}
+static int store_etag(FILE *fp, const anjay_etag_t *etag) {
+    assert(etag);
+    if (fwrite(&etag->size, sizeof(etag->size), 1, fp) != 1) {
+        return -1;
+    }
+    if (etag->size > 0 && fwrite(etag->value, etag->size, 1, fp) != 1) {
+        return -1;
+    }
+    return 0;
+}
 
-static int store_download_state(const download_state_t *state) {
-    FILE *fp = fopen(FW_DOWNLOAD_STATE_NAME, "wb");
-    if (!fp) {
-        fprintf(stderr, "could not open %s for writing\n",
-                FW_DOWNLOAD_STATE_NAME);
-        return -1;
-    }
-    const uint16_t uri_length = strlen(state->persisted_uri);
-    int result = 0;
-    if (fwrite(&uri_length, sizeof(uri_length), 1, fp) != 1
-            || fwrite(state->persisted_uri, uri_length, 1, fp) != 1
-            || fwrite(&state->resume_offset, sizeof(state->resume_offset), 1,
-                      fp) != 1
-            || store_etag(fp, state->resume_etag)) {
-        fprintf(stderr, "could not write firmware download state\n");
-        result = -1;
-    }
-    fclose(fp);
-    if (result) {
-        unlink(FW_DOWNLOAD_STATE_NAME);
-    }
-    return result;
-}
+static int store_download_state(const download_state_t *state) {
+    FILE *fp = fopen(FW_DOWNLOAD_STATE_NAME, "wb");
+    if (!fp) {
+        fprintf(stderr, "could not open %s for writing\n",
+                FW_DOWNLOAD_STATE_NAME);
+        return -1;
+    }
+    const uint16_t uri_length = strlen(state->persisted_uri);
+    int result = 0;
+    if (fwrite(&uri_length, sizeof(uri_length), 1, fp) != 1
+            || fwrite(state->persisted_uri, uri_length, 1, fp) != 1
+            || fwrite(&state->resume_offset, sizeof(state->resume_offset), 1,
+                      fp) != 1
+            || store_etag(fp, state->resume_etag)) {
+        fprintf(stderr, "could not write firmware download state\n");
+        result = -1;
+    }
+    fclose(fp);
+    if (result) {
+        unlink(FW_DOWNLOAD_STATE_NAME);
+    }
+    return result;
+}
 
-static int restore_etag(FILE *fp, anjay_etag_t **out_etag) {
+static int restore_etag(FILE *fp, anjay_etag_t **out_etag) {
     assert(out_etag && !*out_etag); // make sure out_etag is zero-initialized
-    uint8_t size;
-    if (fread(&size, sizeof(size), 1, fp) != 1) {
-        return -1;
-    }
-    anjay_etag_t *etag = anjay_etag_new(size);
-    if (!etag) {
-        return -1;
-    }
+    uint8_t size;
+    if (fread(&size, sizeof(size), 1, fp) != 1) {
+        return -1;
+    }
+    anjay_etag_t *etag = anjay_etag_new(size);
+    if (!etag) {
+        return -1;
+    }
 
-    if (size > 0 && fread(etag->value, size, 1, fp) != 1) {
-        avs_free(etag);
-        return -1;
-    }
-    *out_etag = etag;
-    return 0;
-}
+    if (size > 0 && fread(etag->value, size, 1, fp) != 1) {
+        avs_free(etag);
+        return -1;
+    }
+    *out_etag = etag;
+    return 0;
+}
 
-static int restore_download_state(download_state_t *out_state) {
-    download_state_t data;
-    memset(&data, 0, sizeof(data));
+static int restore_download_state(download_state_t *out_state) {
+    download_state_t data;
+    memset(&data, 0, sizeof(data));
 
-    FILE *fp = fopen(FW_DOWNLOAD_STATE_NAME, "rb");
-    if (!fp) {
-        fprintf(stderr, "could not open %s for reading\n",
-                FW_DOWNLOAD_STATE_NAME);
-        return -1;
-    }
+    FILE *fp = fopen(FW_DOWNLOAD_STATE_NAME, "rb");
+    if (!fp) {
+        fprintf(stderr, "could not open %s for reading\n",
+                FW_DOWNLOAD_STATE_NAME);
+        return -1;
+    }
 
-    int result = 0;
-    uint16_t uri_length;
-    if (fread(&uri_length, sizeof(uri_length), 1, fp) != 1 || uri_length == 0) {
-        result = -1;
-    }
-    if (!result) {
-        data.persisted_uri = (char *) avs_calloc(1, uri_length + 1);
-        if (!data.persisted_uri) {
-            result = -1;
-        }
-    }
-    if (!result
-            && (fread(data.persisted_uri, uri_length, 1, fp) != 1
-                || fread(&data.resume_offset, sizeof(data.resume_offset), 1, fp)
-                           != 1
-                || restore_etag(fp, &data.resume_etag))) {
-        result = -1;
-    }
-    if (result) {
-        fprintf(stderr, "could not restore download state from %s\n",
-                FW_DOWNLOAD_STATE_NAME);
-        avs_free(data.persisted_uri);
-    } else {
-        *out_state = data;
-    }
-    fclose(fp);
-    return result;
-}
+    int result = 0;
+    uint16_t uri_length;
+    if (fread(&uri_length, sizeof(uri_length), 1, fp) != 1 || uri_length == 0) {
+        result = -1;
+    }
+    if (!result) {
+        data.persisted_uri = (char *) avs_calloc(1, uri_length + 1);
+        if (!data.persisted_uri) {
+            result = -1;
+        }
+    }
+    if (!result
+            && (fread(data.persisted_uri, uri_length, 1, fp) != 1
+                || fread(&data.resume_offset, sizeof(data.resume_offset), 1, fp)
+                           != 1
+                || restore_etag(fp, &data.resume_etag))) {
+        result = -1;
+    }
+    if (result) {
+        fprintf(stderr, "could not restore download state from %s\n",
+                FW_DOWNLOAD_STATE_NAME);
+        avs_free(data.persisted_uri);
+    } else {
+        *out_state = data;
+    }
+    fclose(fp);
+    return result;
+}
 
-static void reset_download_state(download_state_t *state) {
-    avs_free(state->persisted_uri);
-    avs_free(state->resume_etag);
-    memset(state, 0, sizeof(*state));
-    unlink(FW_DOWNLOAD_STATE_NAME);
-}
+static void reset_download_state(download_state_t *state) {
+    avs_free(state->persisted_uri);
+    avs_free(state->resume_etag);
+    memset(state, 0, sizeof(*state));
+    unlink(FW_DOWNLOAD_STATE_NAME);
+}
 
-static struct fw_state_t {
-    FILE *firmware_file;
+static struct fw_state_t {
+    FILE *firmware_file;
     // anjay instance this firmware update singleton is associated with
-    anjay_t *anjay;
+    anjay_t *anjay;
     // Current state of the download. It is updated and persited on each
     // fw_stream_write() call.
-    download_state_t download_state;
-} FW_STATE;
+    download_state_t download_state;
+} FW_STATE;
 
-static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
+static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
 

In the next section, we’ll discuss when state storing and restoring should @@ -388,137 +385,137 @@

6.6.3. Persisting firmware state

Keeping all these things in mind, let’s start by refactoring fw_stream_open accordingly:

-
static int fw_open_download_file(long seek_offset) {
+
static int fw_open_download_file(long seek_offset) {
     // It's worth ensuring we start with a NULL firmware_file. In the end
     // it would be our responsibility to manage this pointer, and we want
     // to make sure we never leak any memory.
-    assert(FW_STATE.firmware_file == NULL);
+    assert(FW_STATE.firmware_file == NULL);
     // We're about to create a firmware file for writing
-    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
-    if (!FW_STATE.firmware_file) {
-        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
-        return -1;
-    }
-    if (fseek(FW_STATE.firmware_file, seek_offset, SEEK_SET)) {
-        fprintf(stderr, "Could not seek to %ld\n", seek_offset);
-        fclose(FW_STATE.firmware_file);
-        FW_STATE.firmware_file = NULL;
-        return -1;
-    }
+    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
+    if (!FW_STATE.firmware_file) {
+        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
+        return -1;
+    }
+    if (fseek(FW_STATE.firmware_file, seek_offset, SEEK_SET)) {
+        fprintf(stderr, "Could not seek to %ld\n", seek_offset);
+        fclose(FW_STATE.firmware_file);
+        FW_STATE.firmware_file = NULL;
+        return -1;
+    }
     // We've succeeded
-    return 0;
-}
+    return 0;
+}
 
-static int fw_stream_open(void *user_ptr,
-                          const char *package_uri,
-                          const struct anjay_etag *package_etag) {
+static int fw_stream_open(void *user_ptr,
+                          const char *package_uri,
+                          const struct anjay_etag *package_etag) {
     // We don't use user_ptr.
-    (void) user_ptr;
+    (void) user_ptr;
 
     // We only persist firmware download state if we have both package_uri
     // and package_etag. Otherwise the download could not be resumed.
-    if (package_uri && package_etag) {
-        FW_STATE.download_state.persisted_uri = avs_strdup(package_uri);
-        int result = 0;
-        if (!FW_STATE.download_state.persisted_uri) {
-            fprintf(stderr, "Could not duplicate package URI\n");
-            result = -1;
-        }
-        anjay_etag_t *etag_copy = NULL;
-        if (!result && package_etag) {
-            etag_copy = anjay_etag_clone(package_etag);
-            if (!etag_copy) {
-                fprintf(stderr, "Could not duplicate package ETag\n");
-                result = -1;
-            }
-        }
-        if (!result) {
-            FW_STATE.download_state.resume_etag = etag_copy;
-        } else {
-            reset_download_state(&FW_STATE.download_state);
-            return result;
-        }
-    }
+    if (package_uri && package_etag) {
+        FW_STATE.download_state.persisted_uri = avs_strdup(package_uri);
+        int result = 0;
+        if (!FW_STATE.download_state.persisted_uri) {
+            fprintf(stderr, "Could not duplicate package URI\n");
+            result = -1;
+        }
+        anjay_etag_t *etag_copy = NULL;
+        if (!result && package_etag) {
+            etag_copy = anjay_etag_clone(package_etag);
+            if (!etag_copy) {
+                fprintf(stderr, "Could not duplicate package ETag\n");
+                result = -1;
+            }
+        }
+        if (!result) {
+            FW_STATE.download_state.resume_etag = etag_copy;
+        } else {
+            reset_download_state(&FW_STATE.download_state);
+            return result;
+        }
+    }
 
-    return fw_open_download_file(0);
-}
+    return fw_open_download_file(0);
+}
 

Then, we can implement storing the download state logic in fw_stream_write:

-
static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
-    (void) user_ptr;
+
static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
+    (void) user_ptr;
     // NOTE: fflush() and fsync() are done to be relatively sure that
     // the data is passed to the hardware and so that we can update
     // resume_offset in the download state. They are suboptimal on UNIX-like
     // platforms, and are used just to illustrate when is the right time to
     // update resume_offset on embedded platforms.
-    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1
-            || fflush(FW_STATE.firmware_file)
-            || fsync(fileno(FW_STATE.firmware_file))) {
-        fprintf(stderr, "Writing to firmware image failed\n");
-        return -1;
-    }
-    if (FW_STATE.download_state.persisted_uri) {
-        FW_STATE.download_state.resume_offset += length;
-        if (store_download_state(&FW_STATE.download_state)) {
+    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1
+            || fflush(FW_STATE.firmware_file)
+            || fsync(fileno(FW_STATE.firmware_file))) {
+        fprintf(stderr, "Writing to firmware image failed\n");
+        return -1;
+    }
+    if (FW_STATE.download_state.persisted_uri) {
+        FW_STATE.download_state.resume_offset += length;
+        if (store_download_state(&FW_STATE.download_state)) {
             // If we returned -1 here, the download would be aborted, so it
             // is probably better to continue instead.
-            fprintf(stderr,
-                    "Could not store firmware download state - ignoring\n");
-        }
-    }
-    return 0;
-}
+            fprintf(stderr,
+                    "Could not store firmware download state - ignoring\n");
+        }
+    }
+    return 0;
+}
 

The next step is to make sure that fw_reset resets the download state as well:

-
static void fw_reset(void *user_ptr) {
+
static void fw_reset(void *user_ptr) {
     // Reset can be issued even if the download never started.
-    if (FW_STATE.firmware_file) {
+    if (FW_STATE.firmware_file) {
         // We ignore the result code of fclose(), as fw_reset() can't fail.
-        (void) fclose(FW_STATE.firmware_file);
+        (void) fclose(FW_STATE.firmware_file);
         // and reset our global state to initial value.
-        FW_STATE.firmware_file = NULL;
-    }
+        FW_STATE.firmware_file = NULL;
+    }
     // Finally, let's remove any downloaded payload
-    unlink(FW_IMAGE_DOWNLOAD_NAME);
+    unlink(FW_IMAGE_DOWNLOAD_NAME);
     // And reset any download state.
-    reset_download_state(&FW_STATE.download_state);
-}
+    reset_download_state(&FW_STATE.download_state);
+}
 

And the last piece of the implementation would be to read the download state (if any) at initialization stage, and before installing the firmware update module in Anjay:

-
int fw_update_install(anjay_t *anjay) {
-    anjay_fw_update_initial_state_t state;
-    memset(&state, 0, sizeof(state));
+
int fw_update_install(anjay_t *anjay) {
+    anjay_fw_update_initial_state_t state;
+    memset(&state, 0, sizeof(state));
 
-    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
+    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
         // marker file exists, it means firmware update succeded!
-        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
-        unlink(FW_UPDATED_MARKER);
+        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
+        unlink(FW_UPDATED_MARKER);
         // we can get rid of any download state if the update succeeded
-        reset_download_state(&FW_STATE.download_state);
-    } else if (!restore_download_state(&FW_STATE.download_state)) {
+        reset_download_state(&FW_STATE.download_state);
+    } else if (!restore_download_state(&FW_STATE.download_state)) {
         // download state restored, it means we can try using download
         // resumption
-        if (fw_open_download_file(state.resume_offset)) {
+        if (fw_open_download_file(state.resume_offset)) {
             // the file cannot be opened or seeking failed
-            reset_download_state(&FW_STATE.download_state);
-        } else {
-            state.persisted_uri = FW_STATE.download_state.persisted_uri;
-            state.resume_offset = FW_STATE.download_state.resume_offset;
-            state.resume_etag = FW_STATE.download_state.resume_etag;
-            state.result = ANJAY_FW_UPDATE_INITIAL_DOWNLOADING;
-        }
-    }
+            reset_download_state(&FW_STATE.download_state);
+        } else {
+            state.persisted_uri = FW_STATE.download_state.persisted_uri;
+            state.resume_offset = FW_STATE.download_state.resume_offset;
+            state.resume_etag = FW_STATE.download_state.resume_etag;
+            state.result = ANJAY_FW_UPDATE_INITIAL_DOWNLOADING;
+        }
+    }
     // make sure this module is installed for single Anjay instance only
-    assert(FW_STATE.anjay == NULL);
-    FW_STATE.anjay = anjay;
+    assert(FW_STATE.anjay == NULL);
+    FW_STATE.anjay = anjay;
     // install the module, pass handlers that we implemented and initial state
     // that we discovered upon startup
-    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
-}
+    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
+}
 
diff --git a/FirmwareUpdateTutorial/FU-Introduction.html b/FirmwareUpdateTutorial/FU-Introduction.html index 97857359..91fdfdcc 100644 --- a/FirmwareUpdateTutorial/FU-Introduction.html +++ b/FirmwareUpdateTutorial/FU-Introduction.html @@ -4,13 +4,10 @@ - 6.1. Firmware Update — Anjay 3.4.0 documentation + 6.1. Firmware Update — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
@@ -152,40 +149,40 @@

6.1.2. Firmware update state machine and implementation for the user. At its core, Firmware Update module consists of user-implemented callbacks of various sort. They are shown below, to give an idea on what the implementation of FOTA would take:

-
typedef struct {
+
typedef struct {
     /** Opens the stream that will be used to write the firmware package to;
-     * @ref anjay_fw_update_stream_open_t */
-    anjay_fw_update_stream_open_t *stream_open;
+     * @ref anjay_fw_update_stream_open_t */
+    anjay_fw_update_stream_open_t *stream_open;
     /** Writes data to the download stream;
-     * @ref anjay_fw_update_stream_write_t */
-    anjay_fw_update_stream_write_t *stream_write;
+     * @ref anjay_fw_update_stream_write_t */
+    anjay_fw_update_stream_write_t *stream_write;
     /** Closes the download stream and prepares the firmware package to be
-     * flashed; @ref anjay_fw_update_stream_finish_t */
-    anjay_fw_update_stream_finish_t *stream_finish;
+     * flashed; @ref anjay_fw_update_stream_finish_t */
+    anjay_fw_update_stream_finish_t *stream_finish;
 
     /** Resets the firmware update state and performs any applicable cleanup of
-     * temporary storage if necessary; @ref anjay_fw_update_reset_t */
-    anjay_fw_update_reset_t *reset;
+     * temporary storage if necessary; @ref anjay_fw_update_reset_t */
+    anjay_fw_update_reset_t *reset;
 
     /** Returns the name of downloaded firmware package;
-     * @ref anjay_fw_update_get_name_t */
-    anjay_fw_update_get_name_t *get_name;
+     * @ref anjay_fw_update_get_name_t */
+    anjay_fw_update_get_name_t *get_name;
     /** Return the version of downloaded firmware package;
-     * @ref anjay_fw_update_get_version_t */
-    anjay_fw_update_get_version_t *get_version;
+     * @ref anjay_fw_update_get_version_t */
+    anjay_fw_update_get_version_t *get_version;
 
     /** Performs the actual upgrade with previously downloaded package;
-     * @ref anjay_fw_update_perform_upgrade_t */
-    anjay_fw_update_perform_upgrade_t *perform_upgrade;
+     * @ref anjay_fw_update_perform_upgrade_t */
+    anjay_fw_update_perform_upgrade_t *perform_upgrade;
 
     /** Queries security configuration that shall be used for an encrypted
-     * connection; @ref anjay_fw_update_get_security_config_t */
-    anjay_fw_update_get_security_config_t *get_security_config;
+     * connection; @ref anjay_fw_update_get_security_config_t */
+    anjay_fw_update_get_security_config_t *get_security_config;
 
     /** Queries CoAP transmission parameters to be used during firmware
-     * update; @ref anjay_fw_update_get_coap_tx_params_t */
-    anjay_fw_update_get_coap_tx_params_t *get_coap_tx_params;
-} anjay_fw_update_handlers_t;
+     * update; @ref anjay_fw_update_get_coap_tx_params_t */
+    anjay_fw_update_get_coap_tx_params_t *get_coap_tx_params;
+} anjay_fw_update_handlers_t;
 

Luckily, not all of them need to be implemented during initial diff --git a/FirmwareUpdateTutorial/FU-ModesAndProtocols.html b/FirmwareUpdateTutorial/FU-ModesAndProtocols.html index c6e2796a..7aba191d 100644 --- a/FirmwareUpdateTutorial/FU-ModesAndProtocols.html +++ b/FirmwareUpdateTutorial/FU-ModesAndProtocols.html @@ -4,13 +4,10 @@ - 6.3. Download modes and protocols — Anjay 3.4.0 documentation + 6.3. Download modes and protocols — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
diff --git a/FirmwareUpdateTutorial/FU-PoorConnectivity.html b/FirmwareUpdateTutorial/FU-PoorConnectivity.html index 9c951e35..0acd3b9b 100644 --- a/FirmwareUpdateTutorial/FU-PoorConnectivity.html +++ b/FirmwareUpdateTutorial/FU-PoorConnectivity.html @@ -4,13 +4,10 @@ - 6.5. Poor network connectivity — Anjay 3.4.0 documentation + 6.5. Poor network connectivity — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
diff --git a/FirmwareUpdateTutorial/FU-SecureDownloads.html b/FirmwareUpdateTutorial/FU-SecureDownloads.html index 9c92bb2d..2f80dd7e 100644 --- a/FirmwareUpdateTutorial/FU-SecureDownloads.html +++ b/FirmwareUpdateTutorial/FU-SecureDownloads.html @@ -4,13 +4,10 @@ - 6.4. Secure downloads — Anjay 3.4.0 documentation + 6.4. Secure downloads — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -77,9 +74,9 @@
@@ -126,17 +123,17 @@

6.4.3. Supported security modesThe download can be secured by either PSK, or by Public key certificates.

Security information is configured in Anjay through a structure:

-

The avs_crypto_psk_key_info_t and avs_crypto_psk_identity_info_t are @@ -177,19 +174,19 @@

6.4.3.1. Configuration of PSKavs_net_psk_info_t structure, we may use:

-
avs_net_security_info_t
-avs_net_security_info_from_psk(avs_net_psk_info_t psk);
+
avs_net_security_info_t
+avs_net_security_info_from_psk(avs_net_psk_info_t psk);
 

to convert into into avs_net_security_info_t, as in the following example:

-
avs_net_psk_info_t psk_info = {
-    .key = avs_crypto_psk_key_info_from_buffer(
-            "shared-key", strlen("shared-key")),
-    .identity = avs_crypto_psk_identity_info_from_buffer(
-            "our-identity", strlen("our-identity"))
-};
-avs_net_security_info_t psk_security =
-        avs_net_security_info_from_psk(psk_info);
+
avs_net_psk_info_t psk_info = {
+    .key = avs_crypto_psk_key_info_from_buffer(
+            "shared-key", strlen("shared-key")),
+    .identity = avs_crypto_psk_identity_info_from_buffer(
+            "our-identity", strlen("our-identity"))
+};
+avs_net_security_info_t psk_security =
+        avs_net_security_info_from_psk(psk_info);
 
@@ -199,13 +196,13 @@

6.4.3.2. Configuration of Certificates
/**
  * Configuration for certificate-mode (D)TLS connection.
- */
-typedef struct {
+ */
+typedef struct {
     /**
      * Enables validation of peer certificate chain. If disabled,
      * #ignore_system_trust_store and #trusted_certs are ignored.
-     */
-    bool server_cert_validation;
+     */
+    bool server_cert_validation;
 
     /**
      * Setting this flag to true disables the usage of system-wide trust store
@@ -213,8 +210,8 @@ 

6.4.3.2. Configuration of Certificates * * NOTE: System-wide trust store is currently supported only by the OpenSSL * backend. This field is ignored by the Mbed TLS backend. - */ - bool ignore_system_trust_store; + */ + bool ignore_system_trust_store; /** * Enable use of DNS-based Authentication of Named Entities (DANE) if @@ -222,38 +219,38 @@

6.4.3.2. Configuration of Certificates * * If this field is set to true, but #server_cert_validation is disabled, * "opportunistic DANE" is used. - */ - bool dane; + */ + bool dane; /** * Store of trust anchor certificates. This field is optional and can be * left zero-initialized. If used, it shall be initialized using one of the * <c>avs_crypto_certificate_chain_info_from_*</c> helper functions. - */ - avs_crypto_certificate_chain_info_t trusted_certs; + */ + avs_crypto_certificate_chain_info_t trusted_certs; /** * Store of certificate revocation lists. This field is optional and can be * left zero-initialized. If used, it shall be initialized using one of the * <c>avs_crypto_cert_revocation_list_info_from_*</c> helper functions. - */ - avs_crypto_cert_revocation_list_info_t cert_revocation_lists; + */ + avs_crypto_cert_revocation_list_info_t cert_revocation_lists; /** * Local certificate chain to use for authenticating with the peer. This * field is optional and can be left zero-initialized. If used, it shall be * initialized using one of the * <c>avs_crypto_certificate_chain_info_from_*</c> helper functions. - */ - avs_crypto_certificate_chain_info_t client_cert; + */ + avs_crypto_certificate_chain_info_t client_cert; /** * Private key matching #client_cert to use for authenticating with the * peer. This field is optional and can be left zero-initialized, unless * #client_cert is also specified. If used, it shall be initialized using * one of the <c>avs_crypto_private_key_info_from_*</c> helper functions. - */ - avs_crypto_private_key_info_t client_key; + */ + avs_crypto_private_key_info_t client_key; /** * Enable rebuilding of client certificate chain based on certificates in @@ -263,9 +260,9 @@

6.4.3.2. Configuration of Certificates * #client_cert chain is not self-signed, the library will attempt to find * its ancestors in #trusted_certs and append them to the chain presented * during handshake. - */ - bool rebuild_client_cert_chain; -} avs_net_certificate_info_t; + */ + bool rebuild_client_cert_chain; +} avs_net_certificate_info_t;

To populate it properly, we’re gonna need at least two pieces of information @@ -282,16 +279,16 @@

6.4.3.2. Configuration of Certificates

For example, to configure Certificate based security, loading all information from files, we could do something like this:

-
const avs_net_certificate_info_t cert_info = {
-    .server_cert_validation = true,
-    .trusted_certs = avs_crypto_certificate_chain_info_from_file("./CA.crt"),
-    .client_cert = avs_crypto_certificate_chain_info_from_file("./client.crt"),
+
const avs_net_certificate_info_t cert_info = {
+    .server_cert_validation = true,
+    .trusted_certs = avs_crypto_certificate_chain_info_from_file("./CA.crt"),
+    .client_cert = avs_crypto_certificate_chain_info_from_file("./client.crt"),
     // NOTE: "password" may be NULL if no password is required
-    .client_key =
-            avs_crypto_client_key_info_from_file("./client.key", "password")
-};
-avs_net_security_info_t cert_security =
-        avs_net_security_info_from_certificates(cert_info);
+    .client_key =
+            avs_crypto_client_key_info_from_file("./client.key", "password")
+};
+avs_net_security_info_t cert_security =
+        avs_net_security_info_from_certificates(cert_info);
 
@@ -300,63 +297,63 @@

6.4.3.2. Configuration of Certificates6.4.4. Security configuration with get_security_config callback

Firmware update module provided with Anjay, lets the user implement security configuration per download URI. The relevant API is:

-
typedef int anjay_fw_update_get_security_config_t(
-        void *user_ptr,
-        anjay_security_config_t *out_security_info,
-        const char *download_uri);
+
typedef int anjay_fw_update_get_security_config_t(
+        void *user_ptr,
+        anjay_security_config_t *out_security_info,
+        const char *download_uri);
 

And the corresponding handler in anjay_fw_update_handlers_t to be implemented by the user:

-
typedef struct {
+
typedef struct {
     /** Opens the stream that will be used to write the firmware package to;
-     * @ref anjay_fw_update_stream_open_t */
-    anjay_fw_update_stream_open_t *stream_open;
+     * @ref anjay_fw_update_stream_open_t */
+    anjay_fw_update_stream_open_t *stream_open;
     /** Writes data to the download stream;
-     * @ref anjay_fw_update_stream_write_t */
-    anjay_fw_update_stream_write_t *stream_write;
+     * @ref anjay_fw_update_stream_write_t */
+    anjay_fw_update_stream_write_t *stream_write;
     /** Closes the download stream and prepares the firmware package to be
-     * flashed; @ref anjay_fw_update_stream_finish_t */
-    anjay_fw_update_stream_finish_t *stream_finish;
+     * flashed; @ref anjay_fw_update_stream_finish_t */
+    anjay_fw_update_stream_finish_t *stream_finish;
 
     /** Resets the firmware update state and performs any applicable cleanup of
-     * temporary storage if necessary; @ref anjay_fw_update_reset_t */
-    anjay_fw_update_reset_t *reset;
+     * temporary storage if necessary; @ref anjay_fw_update_reset_t */
+    anjay_fw_update_reset_t *reset;
 
     /** Returns the name of downloaded firmware package;
-     * @ref anjay_fw_update_get_name_t */
-    anjay_fw_update_get_name_t *get_name;
+     * @ref anjay_fw_update_get_name_t */
+    anjay_fw_update_get_name_t *get_name;
     /** Return the version of downloaded firmware package;
-     * @ref anjay_fw_update_get_version_t */
-    anjay_fw_update_get_version_t *get_version;
+     * @ref anjay_fw_update_get_version_t */
+    anjay_fw_update_get_version_t *get_version;
 
     /** Performs the actual upgrade with previously downloaded package;
-     * @ref anjay_fw_update_perform_upgrade_t */
-    anjay_fw_update_perform_upgrade_t *perform_upgrade;
+     * @ref anjay_fw_update_perform_upgrade_t */
+    anjay_fw_update_perform_upgrade_t *perform_upgrade;
 
     /** Queries security configuration that shall be used for an encrypted
-     * connection; @ref anjay_fw_update_get_security_config_t */
-    anjay_fw_update_get_security_config_t *get_security_config;
+     * connection; @ref anjay_fw_update_get_security_config_t */
+    anjay_fw_update_get_security_config_t *get_security_config;
 
     /** Queries CoAP transmission parameters to be used during firmware
-     * update; @ref anjay_fw_update_get_coap_tx_params_t */
-    anjay_fw_update_get_coap_tx_params_t *get_coap_tx_params;
-} anjay_fw_update_handlers_t;
+     * update; @ref anjay_fw_update_get_coap_tx_params_t */
+    anjay_fw_update_get_coap_tx_params_t *get_coap_tx_params;
+} anjay_fw_update_handlers_t;
 

Now, the anjay_fw_update_get_security_config_t job is to fill anjay_security_config_t properly. This structure consists of three fields:

-
typedef struct {
+
typedef struct {
     /**
      * DTLS keys or certificates.
-     */
-    avs_net_security_info_t security_info;
+     */
+    avs_net_security_info_t security_info;
 
     /**
      * Single DANE TLSA record to use for certificate verification, if
      * applicable.
-     */
-    const avs_net_socket_dane_tlsa_record_t *dane_tlsa_record;
+     */
+    const avs_net_socket_dane_tlsa_record_t *dane_tlsa_record;
 
     /**
      * TLS ciphersuites to use.
@@ -364,9 +361,9 @@ 

6.4.4. Security configuration with * A value with <c>num_ids == 0</c> (default) will cause defaults configured * through <c>anjay_configuration_t::default_tls_ciphersuites</c> * to be used. - */ - avs_net_socket_tls_ciphersuites_t tls_ciphersuites; -} anjay_security_config_t; + */ + avs_net_socket_tls_ciphersuites_t tls_ciphersuites; +} anjay_security_config_t;

We’ve already seen in previous sections how to configure @@ -400,167 +397,167 @@

6.4.5. Implementationlast time are highlighted:

-
#include "./firmware_update.h"
+
#include "./firmware_update.h"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
-static struct fw_state_t {
-    FILE *firmware_file;
+static struct fw_state_t {
+    FILE *firmware_file;
     // anjay instance this firmware update singleton is associated with
-    anjay_t *anjay;
-} FW_STATE;
+    anjay_t *anjay;
+} FW_STATE;
 
-static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
+static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
 
-static int fw_stream_open(void *user_ptr,
-                          const char *package_uri,
-                          const struct anjay_etag *package_etag) {
+static int fw_stream_open(void *user_ptr,
+                          const char *package_uri,
+                          const struct anjay_etag *package_etag) {
     // For a moment, we don't need to care about any of the arguments passed.
-    (void) user_ptr;
-    (void) package_uri;
-    (void) package_etag;
+    (void) user_ptr;
+    (void) package_uri;
+    (void) package_etag;
 
     // It's worth ensuring we start with a NULL firmware_file. In the end
     // it would be our responsibility to manage this pointer, and we want
     // to make sure we never leak any memory.
-    assert(FW_STATE.firmware_file == NULL);
+    assert(FW_STATE.firmware_file == NULL);
     // We're about to create a firmware file for writing
-    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
-    if (!FW_STATE.firmware_file) {
-        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
-        return -1;
-    }
+    FW_STATE.firmware_file = fopen(FW_IMAGE_DOWNLOAD_NAME, "wb");
+    if (!FW_STATE.firmware_file) {
+        fprintf(stderr, "Could not open %s\n", FW_IMAGE_DOWNLOAD_NAME);
+        return -1;
+    }
     // We've succeeded
-    return 0;
-}
+    return 0;
+}
 
-static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
-    (void) user_ptr;
+static int fw_stream_write(void *user_ptr, const void *data, size_t length) {
+    (void) user_ptr;
     // We only need to write to file and check if that succeeded
-    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1) {
-        fprintf(stderr, "Writing to firmware image failed\n");
-        return -1;
-    }
-    return 0;
-}
-
-static int fw_stream_finish(void *user_ptr) {
-    (void) user_ptr;
-    assert(FW_STATE.firmware_file != NULL);
-
-    if (fclose(FW_STATE.firmware_file)) {
-        fprintf(stderr, "Closing firmware image failed\n");
-        FW_STATE.firmware_file = NULL;
-        return -1;
-    }
-    FW_STATE.firmware_file = NULL;
-    return 0;
-}
-
-static void fw_reset(void *user_ptr) {
+    if (fwrite(data, length, 1, FW_STATE.firmware_file) != 1) {
+        fprintf(stderr, "Writing to firmware image failed\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int fw_stream_finish(void *user_ptr) {
+    (void) user_ptr;
+    assert(FW_STATE.firmware_file != NULL);
+
+    if (fclose(FW_STATE.firmware_file)) {
+        fprintf(stderr, "Closing firmware image failed\n");
+        FW_STATE.firmware_file = NULL;
+        return -1;
+    }
+    FW_STATE.firmware_file = NULL;
+    return 0;
+}
+
+static void fw_reset(void *user_ptr) {
     // Reset can be issued even if the download never started.
-    if (FW_STATE.firmware_file) {
+    if (FW_STATE.firmware_file) {
         // We ignore the result code of fclose(), as fw_reset() can't fail.
-        (void) fclose(FW_STATE.firmware_file);
+        (void) fclose(FW_STATE.firmware_file);
         // and reset our global state to initial value.
-        FW_STATE.firmware_file = NULL;
-    }
+        FW_STATE.firmware_file = NULL;
+    }
     // Finally, let's remove any downloaded payload
-    unlink(FW_IMAGE_DOWNLOAD_NAME);
-}
+    unlink(FW_IMAGE_DOWNLOAD_NAME);
+}
 
 // A part of a rather simple logic checking if the firmware update was
 // successfully performed.
-static const char *FW_UPDATED_MARKER = "/tmp/fw-updated-marker";
-
-static int fw_perform_upgrade(void *user_ptr) {
-    if (chmod(FW_IMAGE_DOWNLOAD_NAME, 0700) == -1) {
-        fprintf(stderr,
-                "Could not make firmware executable: %s\n",
-                strerror(errno));
-        return -1;
-    }
+static const char *FW_UPDATED_MARKER = "/tmp/fw-updated-marker";
+
+static int fw_perform_upgrade(void *user_ptr) {
+    if (chmod(FW_IMAGE_DOWNLOAD_NAME, 0700) == -1) {
+        fprintf(stderr,
+                "Could not make firmware executable: %s\n",
+                strerror(errno));
+        return -1;
+    }
     // Create a marker file, so that the new process knows it is the "upgraded"
     // one
-    FILE *marker = fopen(FW_UPDATED_MARKER, "w");
-    if (!marker) {
-        fprintf(stderr, "Marker file could not be created\n");
-        return -1;
-    }
-    fclose(marker);
-
-    assert(ENDPOINT_NAME);
+    FILE *marker = fopen(FW_UPDATED_MARKER, "w");
+    if (!marker) {
+        fprintf(stderr, "Marker file could not be created\n");
+        return -1;
+    }
+    fclose(marker);
+
+    assert(ENDPOINT_NAME);
     // If the call below succeeds, the firmware is considered as "upgraded",
     // and we hope the newly started client registers to the Server.
-    (void) execl(FW_IMAGE_DOWNLOAD_NAME, FW_IMAGE_DOWNLOAD_NAME, ENDPOINT_NAME,
-                 NULL);
-    fprintf(stderr, "execl() failed: %s\n", strerror(errno));
+    (void) execl(FW_IMAGE_DOWNLOAD_NAME, FW_IMAGE_DOWNLOAD_NAME, ENDPOINT_NAME,
+                 NULL);
+    fprintf(stderr, "execl() failed: %s\n", strerror(errno));
     // If we are here, it means execl() failed. Marker file MUST now be removed,
     // as the firmware update failed.
-    unlink(FW_UPDATED_MARKER);
-    return -1;
-}
-
-static int fw_get_security_config(void *user_ptr,
-                                  anjay_security_config_t *out_security_info,
-                                  const char *download_uri) {
-    (void) user_ptr;
-    if (!anjay_security_config_from_dm(FW_STATE.anjay, out_security_info,
-                                       download_uri)) {
+    unlink(FW_UPDATED_MARKER);
+    return -1;
+}
+
+static int fw_get_security_config(void *user_ptr,
+                                  anjay_security_config_t *out_security_info,
+                                  const char *download_uri) {
+    (void) user_ptr;
+    if (!anjay_security_config_from_dm(FW_STATE.anjay, out_security_info,
+                                       download_uri)) {
         // found a match
-        return 0;
-    }
+        return 0;
+    }
 
     // no match found, fallback to loading certificates from given paths
-    memset(out_security_info, 0, sizeof(*out_security_info));
-    const avs_net_certificate_info_t cert_info = {
-        .server_cert_validation = true,
-        .trusted_certs =
-                avs_crypto_certificate_chain_info_from_file("./certs/CA.crt"),
-        .client_cert = avs_crypto_certificate_chain_info_from_file(
-                "./certs/client.crt"),
-        .client_key = avs_crypto_private_key_info_from_file(
-                "./certs/client.key", NULL)
-    };
+    memset(out_security_info, 0, sizeof(*out_security_info));
+    const avs_net_certificate_info_t cert_info = {
+        .server_cert_validation = true,
+        .trusted_certs =
+                avs_crypto_certificate_chain_info_from_file("./certs/CA.crt"),
+        .client_cert = avs_crypto_certificate_chain_info_from_file(
+                "./certs/client.crt"),
+        .client_key = avs_crypto_private_key_info_from_file(
+                "./certs/client.key", NULL)
+    };
     // NOTE: this assignment is safe, because cert_info contains pointers to
     // string literals only. If the configuration were to load certificate info
     // from buffers they would have to be stored somewhere - e.g. on the heap.
-    out_security_info->security_info =
-            avs_net_security_info_from_certificates(cert_info);
-    return 0;
-}
+    out_security_info->security_info =
+            avs_net_security_info_from_certificates(cert_info);
+    return 0;
+}
 
-static const anjay_fw_update_handlers_t HANDLERS = {
-    .stream_open = fw_stream_open,
-    .stream_write = fw_stream_write,
-    .stream_finish = fw_stream_finish,
-    .reset = fw_reset,
-    .perform_upgrade = fw_perform_upgrade,
-    .get_security_config = fw_get_security_config
-};
-
-const char *ENDPOINT_NAME = NULL;
-
-int fw_update_install(anjay_t *anjay) {
-    anjay_fw_update_initial_state_t state;
-    memset(&state, 0, sizeof(state));
-
-    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
+static const anjay_fw_update_handlers_t HANDLERS = {
+    .stream_open = fw_stream_open,
+    .stream_write = fw_stream_write,
+    .stream_finish = fw_stream_finish,
+    .reset = fw_reset,
+    .perform_upgrade = fw_perform_upgrade,
+    .get_security_config = fw_get_security_config
+};
+
+const char *ENDPOINT_NAME = NULL;
+
+int fw_update_install(anjay_t *anjay) {
+    anjay_fw_update_initial_state_t state;
+    memset(&state, 0, sizeof(state));
+
+    if (access(FW_UPDATED_MARKER, F_OK) != -1) {
         // marker file exists, it means firmware update succeded!
-        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
-        unlink(FW_UPDATED_MARKER);
-    }
+        state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
+        unlink(FW_UPDATED_MARKER);
+    }
     // make sure this module is installed for single Anjay instance only
-    assert(FW_STATE.anjay == NULL);
-    FW_STATE.anjay = anjay;
+    assert(FW_STATE.anjay == NULL);
+    FW_STATE.anjay = anjay;
     // install the module, pass handlers that we implemented and initial state
     // that we discovered upon startup
-    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
-}
+    return anjay_fw_update_install(anjay, &HANDLERS, NULL, &state);
+}
 
diff --git a/FirmwareUpdateTutorial/FU1.html b/FirmwareUpdateTutorial/FU1.html index 16393cfb..5ded02a8 100644 --- a/FirmwareUpdateTutorial/FU1.html +++ b/FirmwareUpdateTutorial/FU1.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/FirmwareUpdateTutorial/FU2.html b/FirmwareUpdateTutorial/FU2.html index 79e35dd9..1864e831 100644 --- a/FirmwareUpdateTutorial/FU2.html +++ b/FirmwareUpdateTutorial/FU2.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/FirmwareUpdateTutorial/FU3.html b/FirmwareUpdateTutorial/FU3.html index 98d060e4..a24a6e79 100644 --- a/FirmwareUpdateTutorial/FU3.html +++ b/FirmwareUpdateTutorial/FU3.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/FirmwareUpdateTutorial/FU4.html b/FirmwareUpdateTutorial/FU4.html index 6afb7440..68009332 100644 --- a/FirmwareUpdateTutorial/FU4.html +++ b/FirmwareUpdateTutorial/FU4.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/FirmwareUpdateTutorial/FU5.html b/FirmwareUpdateTutorial/FU5.html index b2064d3e..1899fcda 100644 --- a/FirmwareUpdateTutorial/FU5.html +++ b/FirmwareUpdateTutorial/FU5.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/FirmwareUpdateTutorial/FU6.html b/FirmwareUpdateTutorial/FU6.html index 5eeca600..13a30c56 100644 --- a/FirmwareUpdateTutorial/FU6.html +++ b/FirmwareUpdateTutorial/FU6.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
- 3.4.0 + 3.4.1
@@ -67,8 +64,8 @@
    -
  • - +
  • »
  • +
  • Redirection
diff --git a/Introduction.html b/Introduction.html index 54924869..5b56bcbf 100644 --- a/Introduction.html +++ b/Introduction.html @@ -4,13 +4,10 @@ - 1. Introduction — Anjay 3.4.0 documentation + 1. Introduction — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -68,8 +65,8 @@
    -
  • - +
  • »
  • +
  • 1. Introduction
diff --git a/LwM2M.html b/LwM2M.html index 3f5e525e..82fe41fb 100644 --- a/LwM2M.html +++ b/LwM2M.html @@ -4,13 +4,10 @@ - 2. OMA LwM2M - Brief description — Anjay 3.4.0 documentation + 2. OMA LwM2M - Brief description — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -68,8 +65,8 @@
    -
  • - +
  • »
  • +
  • 2. OMA LwM2M - Brief description
diff --git a/Migrating.html b/Migrating.html index 6e2918f0..dfe773e1 100644 --- a/Migrating.html +++ b/Migrating.html @@ -4,13 +4,10 @@ - 9. Migrating from older versions — Anjay 3.4.0 documentation + 9. Migrating from older versions — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -81,8 +78,8 @@
    -
  • - +
  • »
  • +
  • 9. Migrating from older versions
diff --git a/Migrating/MigratingCustomEntropy.html b/Migrating/MigratingCustomEntropy.html index 733662eb..6caeddb5 100644 --- a/Migrating/MigratingCustomEntropy.html +++ b/Migrating/MigratingCustomEntropy.html @@ -4,13 +4,10 @@ - 9.2. Migrating mbed TLS custom entropy initializers — Anjay 3.4.0 documentation + 9.2. Migrating mbed TLS custom entropy initializers — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
- 3.4.0 + 3.4.1
@@ -81,9 +78,9 @@
@@ -99,26 +96,26 @@

9.2. Migrating mbed TLS custom entropy i
// NOTE: Code compatible with Anjay <=2.2, avs_commons <=4.0
 // WITH_MBEDTLS_CUSTOM_ENTROPY_INITIALIZER needs to be enabled at compile time
 
-#include <avsystem/commons/defs.h>
-#include <mbedtls/entropy.h>
-#include <mbedtls/entropy_poll.h>
+#include <avsystem/commons/defs.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/entropy_poll.h>
 
-static int entropy_poll(void *user_arg,
-                        uint8_t *output,
-                        size_t len,
-                        size_t *out_len) {
+static int entropy_poll(void *user_arg,
+                        uint8_t *output,
+                        size_t len,
+                        size_t *out_len) {
     // TODO: Platform-specific entropy collection code
-}
+}
 
 // NOTE: For Anjay 1.x / avs_commons 3.x, return type was "int"
-avs_error_t avs_net_mbedtls_entropy_init(mbedtls_entropy_context *entropy) {
-    if (mbedtls_entropy_add_source(entropy, entropy_poll, NULL,
-                                   MBEDTLS_ENTROPY_MIN_PLATFORM,
-                                   MBEDTLS_ENTROPY_SOURCE_STRONG)) {
-        return avs_errno(AVS_UNKNOWN_ERROR);
-    }
-    return AVS_OK;
-}
+avs_error_t avs_net_mbedtls_entropy_init(mbedtls_entropy_context *entropy) {
+    if (mbedtls_entropy_add_source(entropy, entropy_poll, NULL,
+                                   MBEDTLS_ENTROPY_MIN_PLATFORM,
+                                   MBEDTLS_ENTROPY_SOURCE_STRONG)) {
+        return avs_errno(AVS_UNKNOWN_ERROR);
+    }
+    return AVS_OK;
+}
 

This mechanism has been removed in avs_commons 4.1, and as such is not @@ -127,53 +124,53 @@

9.2. Migrating mbed TLS custom entropy i PRNG context, for example as follows:

// Code compatible with Anjay >=2.3, avs_commons >=4.1
 
-#include <anjay/core.h>
-#include <avsystem/commons/avs_prng.h>
-#include <mbedtls/entropy.h>
-#include <mbedtls/entropy_poll.h>
+#include <anjay/core.h>
+#include <avsystem/commons/avs_prng.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/entropy_poll.h>
 
-static mbedtls_entropy_context g_entropy_context;
+static mbedtls_entropy_context g_entropy_context;
 
-static int entropy_poll(void *user_arg,
-                        uint8_t *output,
-                        size_t len,
-                        size_t *out_len) {
+static int entropy_poll(void *user_arg,
+                        uint8_t *output,
+                        size_t len,
+                        size_t *out_len) {
     // TODO: Platform-specific entropy collection code
-}
+}
 
-static int entropy_callback(unsigned char *out_buf,
-                            size_t out_buf_len,
-                            void *dummy_user_arg) {
-    (void) dummy_user_arg;
-    return mbedtls_entropy_func(&g_entropy_context, out_buf, out_buf_len);
-}
+static int entropy_callback(unsigned char *out_buf,
+                            size_t out_buf_len,
+                            void *dummy_user_arg) {
+    (void) dummy_user_arg;
+    return mbedtls_entropy_func(&g_entropy_context, out_buf, out_buf_len);
+}
 
-int main() {
+int main() {
     // ... before initializing Anjay ...
-    avs_crypto_prng_ctx_t *prng_ctx;
-    mbedtls_entropy_init(&g_entropy_context);
-    if (mbedtls_entropy_add_source(&g_entropy_context, entropy_poll, NULL,
-                                   MBEDTLS_ENTROPY_MIN_PLATFORM,
-                                   MBEDTLS_ENTROPY_SOURCE_STRONG)
-             || !(prng_ctx = avs_crypto_prng_new(entropy_callback, NULL))) {
+    avs_crypto_prng_ctx_t *prng_ctx;
+    mbedtls_entropy_init(&g_entropy_context);
+    if (mbedtls_entropy_add_source(&g_entropy_context, entropy_poll, NULL,
+                                   MBEDTLS_ENTROPY_MIN_PLATFORM,
+                                   MBEDTLS_ENTROPY_SOURCE_STRONG)
+             || !(prng_ctx = avs_crypto_prng_new(entropy_callback, NULL))) {
         // TODO: Better error handling
-        return -1;
-    }
+        return -1;
+    }
 
     // ... when initializing Anjay ...
-    const anjay_configuration_t anjay_config = {
+    const anjay_configuration_t anjay_config = {
         // TODO: Other configuration options
-        .prng_ctx = prng_ctx
-    };
-    anjay_t *anjay = anjay_new(&anjay_config);
+        .prng_ctx = prng_ctx
+    };
+    anjay_t *anjay = anjay_new(&anjay_config);
 
     // ... when cleaning up Anjay ...
-    anjay_delete(anjay);
-    avs_crypto_prng_free(&prng_ctx);
+    anjay_delete(anjay);
+    avs_crypto_prng_free(&prng_ctx);
 
     // ...
-    return 0;
-}
+    return 0;
+}
 

If you’re using avs_coap contexts and/or raw avs_net sockets in addition diff --git a/Migrating/MigratingFromAnjay214.html b/Migrating/MigratingFromAnjay214.html index 4cb4d70a..e14774f4 100644 --- a/Migrating/MigratingFromAnjay214.html +++ b/Migrating/MigratingFromAnjay214.html @@ -4,13 +4,10 @@ - 9.7. Migrating from Anjay 2.9.x-2.14.x — Anjay 3.4.0 documentation + 9.7. Migrating from Anjay 2.9.x-2.14.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

- 3.4.0 + 3.4.1
@@ -81,9 +78,9 @@
@@ -287,71 +284,71 @@

9.7.3.3.
/**
  * A PSK/identity pair with borrowed pointers. avs_commons will never attempt
  * to modify these values.
- */
-typedef struct {
-    const void *psk;
-    size_t psk_size;
-    const void *identity;
-    size_t identity_size;
-} avs_net_psk_info_t;
+ */
+typedef struct {
+    const void *psk;
+    size_t psk_size;
+    const void *identity;
+    size_t identity_size;
+} avs_net_psk_info_t;
 
 // ...
 
-typedef struct {
-    avs_net_security_mode_t mode;
-    union {
-        avs_net_psk_info_t psk;
-        avs_net_certificate_info_t cert;
-    } data;
-} avs_net_security_info_t;
+typedef struct {
+    avs_net_security_mode_t mode;
+    union {
+        avs_net_psk_info_t psk;
+        avs_net_certificate_info_t cert;
+    } data;
+} avs_net_security_info_t;
 
-avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
+avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
 

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • @@ -362,10 +359,10 @@

    9.7.3.3.

    9.7.3.4. Additional function in the hardware security engine API

    A new API has been added to the hardware security engine API in avs_commons:

    -
    avs_error_t
    -avs_crypto_pki_engine_key_store(const char *query,
    -                                const avs_crypto_private_key_info_t *key_info,
    -                                avs_crypto_prng_ctx_t *prng_ctx);
    +
    avs_error_t
    +avs_crypto_pki_engine_key_store(const char *query,
    +                                const avs_crypto_private_key_info_t *key_info,
    +                                avs_crypto_prng_ctx_t *prng_ctx);
     

    If you implement your own hardware security engine backend implementation, you may diff --git a/Migrating/MigratingFromAnjay215.html b/Migrating/MigratingFromAnjay215.html index dad02419..20f3ea13 100644 --- a/Migrating/MigratingFromAnjay215.html +++ b/Migrating/MigratingFromAnjay215.html @@ -4,13 +4,10 @@ - 9.8. Migrating from Anjay 2.15.x — Anjay 3.4.0 documentation + 9.8. Migrating from Anjay 2.15.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    diff --git a/Migrating/MigratingFromAnjay225.html b/Migrating/MigratingFromAnjay225.html index b2dd0bec..c4572962 100644 --- a/Migrating/MigratingFromAnjay225.html +++ b/Migrating/MigratingFromAnjay225.html @@ -4,13 +4,10 @@ - 9.1. Migrating from Anjay 2.2.5 — Anjay 3.4.0 documentation + 9.1. Migrating from Anjay 2.2.5 — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    @@ -165,17 +162,17 @@

    9.1.3.1.

    Execute argument value getter

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • diff --git a/Migrating/MigratingFromAnjay24.html b/Migrating/MigratingFromAnjay24.html index 220d9715..8ab04b7e 100644 --- a/Migrating/MigratingFromAnjay24.html +++ b/Migrating/MigratingFromAnjay24.html @@ -4,13 +4,10 @@ - 9.3. Migrating from Anjay 2.3.x or 2.4.x — Anjay 3.4.0 documentation + 9.3. Migrating from Anjay 2.3.x or 2.4.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    @@ -161,15 +158,15 @@

    9.3.3.1.

    Getter function for retrieving security information from data model

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • diff --git a/Migrating/MigratingFromAnjay26.html b/Migrating/MigratingFromAnjay26.html index cc8cce26..ad5bc4b6 100644 --- a/Migrating/MigratingFromAnjay26.html +++ b/Migrating/MigratingFromAnjay26.html @@ -4,13 +4,10 @@ - 9.4. Migrating from Anjay 2.5.x or 2.6.x — Anjay 3.4.0 documentation + 9.4. Migrating from Anjay 2.5.x or 2.6.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    @@ -160,15 +157,15 @@

    9.4.3.1.

    Getter function for retrieving security information from data model

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • diff --git a/Migrating/MigratingFromAnjay27.html b/Migrating/MigratingFromAnjay27.html index e10e587a..7d74872b 100644 --- a/Migrating/MigratingFromAnjay27.html +++ b/Migrating/MigratingFromAnjay27.html @@ -4,13 +4,10 @@ - 9.5. Migrating from Anjay 2.7.x — Anjay 3.4.0 documentation + 9.5. Migrating from Anjay 2.7.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    @@ -310,71 +307,71 @@

    9.5.4.4.
    /**
      * A PSK/identity pair with borrowed pointers. avs_commons will never attempt
      * to modify these values.
    - */
    -typedef struct {
    -    const void *psk;
    -    size_t psk_size;
    -    const void *identity;
    -    size_t identity_size;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    const void *psk;
    +    size_t psk_size;
    +    const void *identity;
    +    size_t identity_size;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • diff --git a/Migrating/MigratingFromAnjay28.html b/Migrating/MigratingFromAnjay28.html index 7d016125..dc20f1c5 100644 --- a/Migrating/MigratingFromAnjay28.html +++ b/Migrating/MigratingFromAnjay28.html @@ -4,13 +4,10 @@ - 9.6. Migrating from Anjay 2.8.x — Anjay 3.4.0 documentation + 9.6. Migrating from Anjay 2.8.x — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    @@ -331,71 +328,71 @@

    9.6.4.4.
    /**
      * A PSK/identity pair with borrowed pointers. avs_commons will never attempt
      * to modify these values.
    - */
    -typedef struct {
    -    const void *psk;
    -    size_t psk_size;
    -    const void *identity;
    -    size_t identity_size;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    const void *psk;
    +    size_t psk_size;
    +    const void *identity;
    +    size_t identity_size;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     

  • New API:

    -
    typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_identity_info_t;
    +
    typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_identity_info_t;
     
     // ...
     
    -avs_crypto_psk_identity_info_t
    -avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    -                                         size_t buffer_size);
    +avs_crypto_psk_identity_info_t
    +avs_crypto_psk_identity_info_from_buffer(const void *buffer,
    +                                         size_t buffer_size);
     
     // ...
     
    -typedef struct {
    -    avs_crypto_security_info_union_t desc;
    -} avs_crypto_psk_key_info_t;
    +typedef struct {
    +    avs_crypto_security_info_union_t desc;
    +} avs_crypto_psk_key_info_t;
     
     // ...
     
    -avs_crypto_psk_key_info_t
    -avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
    +avs_crypto_psk_key_info_t
    +avs_crypto_psk_key_info_from_buffer(const void *buffer, size_t buffer_size);
     
    /**
      * A PSK/identity pair. avs_commons will never attempt to modify these values.
    - */
    -typedef struct {
    -    avs_crypto_psk_key_info_t key;
    -    avs_crypto_psk_identity_info_t identity;
    -} avs_net_psk_info_t;
    + */
    +typedef struct {
    +    avs_crypto_psk_key_info_t key;
    +    avs_crypto_psk_identity_info_t identity;
    +} avs_net_psk_info_t;
     
     // ...
     
    -typedef struct {
    -    avs_net_security_mode_t mode;
    -    union {
    -        avs_net_psk_info_t psk;
    -        avs_net_certificate_info_t cert;
    -    } data;
    -} avs_net_security_info_t;
    +typedef struct {
    +    avs_net_security_mode_t mode;
    +    union {
    +        avs_net_psk_info_t psk;
    +        avs_net_certificate_info_t cert;
    +    } data;
    +} avs_net_security_info_t;
     
    -avs_net_security_info_t
    -avs_net_security_info_from_psk(avs_net_psk_info_t psk);
    +avs_net_security_info_t
    +avs_net_security_info_from_psk(avs_net_psk_info_t psk);
     
  • @@ -416,10 +413,10 @@

    9.6.4.5.

    9.6.4.6. Additional function in the hardware security engine API

    A new API has been added to the hardware security engine API in avs_commons:

    -
    avs_error_t
    -avs_crypto_pki_engine_key_store(const char *query,
    -                                const avs_crypto_private_key_info_t *key_info,
    -                                avs_crypto_prng_ctx_t *prng_ctx);
    +
    avs_error_t
    +avs_crypto_pki_engine_key_store(const char *query,
    +                                const avs_crypto_private_key_info_t *key_info,
    +                                avs_crypto_prng_ctx_t *prng_ctx);
     

    If you implement your own hardware security engine backend implementation, you may diff --git a/Migrating/MigratingFromAnjay30.html b/Migrating/MigratingFromAnjay30.html index ba81d11c..5c749f67 100644 --- a/Migrating/MigratingFromAnjay30.html +++ b/Migrating/MigratingFromAnjay30.html @@ -4,13 +4,10 @@ - 9.9. Migrating from Anjay 3.0 — Anjay 3.4.0 documentation + 9.9. Migrating from Anjay 3.0 — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    diff --git a/Migrating/MigratingFromAnjay32.html b/Migrating/MigratingFromAnjay32.html index 8b9cf6d0..2d839080 100644 --- a/Migrating/MigratingFromAnjay32.html +++ b/Migrating/MigratingFromAnjay32.html @@ -4,13 +4,10 @@ - 9.10. Migrating from Anjay 3.1 or 3.2 — Anjay 3.4.0 documentation + 9.10. Migrating from Anjay 3.1 or 3.2 — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    diff --git a/Migrating/MigratingFromAnjay33.html b/Migrating/MigratingFromAnjay33.html index 390104c1..daf40974 100644 --- a/Migrating/MigratingFromAnjay33.html +++ b/Migrating/MigratingFromAnjay33.html @@ -4,13 +4,10 @@ - 9.11. Migrating from Anjay 3.3 — Anjay 3.4.0 documentation + 9.11. Migrating from Anjay 3.3 — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -81,9 +78,9 @@
    diff --git a/PortingGuideForNonPOSIXPlatforms.html b/PortingGuideForNonPOSIXPlatforms.html index af27f355..4d9561f5 100644 --- a/PortingGuideForNonPOSIXPlatforms.html +++ b/PortingGuideForNonPOSIXPlatforms.html @@ -4,13 +4,10 @@ - 8. Porting guide for non-POSIX platforms — Anjay 3.4.0 documentation + 8. Porting guide for non-POSIX platforms — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -74,8 +71,8 @@
      -
    • - +
    • »
    • +
    • 8. Porting guide for non-POSIX platforms
    diff --git a/PortingGuideForNonPOSIXPlatforms/CustomTLS.html b/PortingGuideForNonPOSIXPlatforms/CustomTLS.html index 31fc2dcf..10af5c7b 100644 --- a/PortingGuideForNonPOSIXPlatforms/CustomTLS.html +++ b/PortingGuideForNonPOSIXPlatforms/CustomTLS.html @@ -4,13 +4,10 @@ - 8.4. Custom (D)TLS layers — Anjay 3.4.0 documentation + 8.4. Custom (D)TLS layers — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -83,9 +80,9 @@
    @@ -183,8 +180,8 @@

    8.4.3. List of functions to implementImplementations of the following functions will need to be provided:

    -
    static avs_error_t configure_tls_version(tls_socket_impl_t *sock,
    -                                         avs_net_ssl_version_t version) {
    -    switch (version) {
    -    case AVS_NET_SSL_VERSION_DEFAULT:
    -    case AVS_NET_SSL_VERSION_SSLv2_OR_3:
    -        return AVS_OK;
    -    case AVS_NET_SSL_VERSION_SSLv3:
    -        SSL_CTX_set_min_proto_version(sock->ctx, SSL3_VERSION);
    -        return AVS_OK;
    -    case AVS_NET_SSL_VERSION_TLSv1:
    -        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_VERSION);
    -        return AVS_OK;
    -    case AVS_NET_SSL_VERSION_TLSv1_1:
    -        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_1_VERSION);
    -        return AVS_OK;
    -    case AVS_NET_SSL_VERSION_TLSv1_2:
    -        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_2_VERSION);
    -        return AVS_OK;
    -    case AVS_NET_SSL_VERSION_TLSv1_3:
    -        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_3_VERSION);
    -        return AVS_OK;
    -    default:
    -        return avs_errno(AVS_ENOTSUP);
    -    }
    -}
    +
    static avs_error_t configure_tls_version(tls_socket_impl_t *sock,
    +                                         avs_net_ssl_version_t version) {
    +    switch (version) {
    +    case AVS_NET_SSL_VERSION_DEFAULT:
    +    case AVS_NET_SSL_VERSION_SSLv2_OR_3:
    +        return AVS_OK;
    +    case AVS_NET_SSL_VERSION_SSLv3:
    +        SSL_CTX_set_min_proto_version(sock->ctx, SSL3_VERSION);
    +        return AVS_OK;
    +    case AVS_NET_SSL_VERSION_TLSv1:
    +        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_VERSION);
    +        return AVS_OK;
    +    case AVS_NET_SSL_VERSION_TLSv1_1:
    +        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_1_VERSION);
    +        return AVS_OK;
    +    case AVS_NET_SSL_VERSION_TLSv1_2:
    +        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_2_VERSION);
    +        return AVS_OK;
    +    case AVS_NET_SSL_VERSION_TLSv1_3:
    +        SSL_CTX_set_min_proto_version(sock->ctx, TLS1_3_VERSION);
    +        return AVS_OK;
    +    default:
    +        return avs_errno(AVS_ENOTSUP);
    +    }
    +}
     
    @@ -435,88 +432,88 @@

    8.4.1.7.5. <

    In OpenSSL, a different type of BIO object is used for TLS and DTLS protocols. perform_handshake() function must thus be updated accordingly, so that BIO_new_dgram() is used for DTLS and BIO_new_socket() for TLS:

    -
    static avs_error_t perform_handshake(tls_socket_impl_t *sock,
    -                                     const char *host) {
    -    union {
    -        struct sockaddr addr;
    -        struct sockaddr_storage storage;
    -    } peername;
    -    const void *fd_ptr = avs_net_socket_get_system(sock->backend_socket);
    -    if (!fd_ptr
    -            || getpeername(*(const int *) fd_ptr, &peername.addr,
    -                           &(socklen_t) { sizeof(peername) })) {
    -        return avs_errno(AVS_EBADF);
    -    }
    +
    static avs_error_t perform_handshake(tls_socket_impl_t *sock,
    +                                     const char *host) {
    +    union {
    +        struct sockaddr addr;
    +        struct sockaddr_storage storage;
    +    } peername;
    +    const void *fd_ptr = avs_net_socket_get_system(sock->backend_socket);
    +    if (!fd_ptr
    +            || getpeername(*(const int *) fd_ptr, &peername.addr,
    +                           &(socklen_t) { sizeof(peername) })) {
    +        return avs_errno(AVS_EBADF);
    +    }
     
    -    sock->ssl = SSL_new(sock->ctx);
    -    if (!sock->ssl) {
    -        return avs_errno(AVS_ENOMEM);
    -    }
    +    sock->ssl = SSL_new(sock->ctx);
    +    if (!sock->ssl) {
    +        return avs_errno(AVS_ENOMEM);
    +    }
     
    -    SSL_set_app_data(sock->ssl, sock);
    -    if (sock->dane_enabled) {
    +    SSL_set_app_data(sock->ssl, sock);
    +    if (sock->dane_enabled) {
             // NOTE: SSL_dane_enable() calls SSL_set_tlsext_host_name() internally
    -        SSL_dane_enable(sock->ssl, host);
    -        bool have_usable_tlsa_records = false;
    -        for (size_t i = 0; i < sock->dane_tlsa_array_size; ++i) {
    -            if (SSL_CTX_get_verify_mode(sock->ctx) == SSL_VERIFY_NONE
    -                    && (sock->dane_tlsa_array[i].certificate_usage
    -                                == AVS_NET_SOCKET_DANE_CA_CONSTRAINT
    -                        || sock->dane_tlsa_array[i].certificate_usage
    -                                   == AVS_NET_SOCKET_DANE_SERVICE_CERTIFICATE_CONSTRAINT)) {
    +        SSL_dane_enable(sock->ssl, host);
    +        bool have_usable_tlsa_records = false;
    +        for (size_t i = 0; i < sock->dane_tlsa_array_size; ++i) {
    +            if (SSL_CTX_get_verify_mode(sock->ctx) == SSL_VERIFY_NONE
    +                    && (sock->dane_tlsa_array[i].certificate_usage
    +                                == AVS_NET_SOCKET_DANE_CA_CONSTRAINT
    +                        || sock->dane_tlsa_array[i].certificate_usage
    +                                   == AVS_NET_SOCKET_DANE_SERVICE_CERTIFICATE_CONSTRAINT)) {
                     // PKIX-TA and PKIX-EE constraints are unusable for
                     // opportunistic clients
    -                continue;
    -            }
    -            SSL_dane_tlsa_add(
    -                    sock->ssl,
    -                    (uint8_t) sock->dane_tlsa_array[i].certificate_usage,
    -                    (uint8_t) sock->dane_tlsa_array[i].selector,
    -                    (uint8_t) sock->dane_tlsa_array[i].matching_type,
    -                    (unsigned const char *) sock->dane_tlsa_array[i]
    -                            .association_data,
    -                    sock->dane_tlsa_array[i].association_data_size);
    -            have_usable_tlsa_records = true;
    -        }
    -        if (SSL_CTX_get_verify_mode(sock->ctx) == SSL_VERIFY_NONE
    -                && have_usable_tlsa_records) {
    -            SSL_set_verify(sock->ssl, SSL_VERIFY_PEER, NULL);
    -        }
    -    } else {
    -        SSL_set_tlsext_host_name(sock->ssl, host);
    -    }
    -    SSL_set1_host(sock->ssl, host);
    +                continue;
    +            }
    +            SSL_dane_tlsa_add(
    +                    sock->ssl,
    +                    (uint8_t) sock->dane_tlsa_array[i].certificate_usage,
    +                    (uint8_t) sock->dane_tlsa_array[i].selector,
    +                    (uint8_t) sock->dane_tlsa_array[i].matching_type,
    +                    (unsigned const char *) sock->dane_tlsa_array[i]
    +                            .association_data,
    +                    sock->dane_tlsa_array[i].association_data_size);
    +            have_usable_tlsa_records = true;
    +        }
    +        if (SSL_CTX_get_verify_mode(sock->ctx) == SSL_VERIFY_NONE
    +                && have_usable_tlsa_records) {
    +            SSL_set_verify(sock->ssl, SSL_VERIFY_PEER, NULL);
    +        }
    +    } else {
    +        SSL_set_tlsext_host_name(sock->ssl, host);
    +    }
    +    SSL_set1_host(sock->ssl, host);
     
    -    BIO *bio = NULL;
    -    if (sock->backend_type == AVS_NET_UDP_SOCKET) {
    -        bio = BIO_new_dgram(*(const int *) fd_ptr, 0);
    -        BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &peername.addr);
    -        DTLS_set_timer_cb(sock->ssl, dtls_timer_cb);
    -    } else {
    -        bio = BIO_new_socket(*(const int *) fd_ptr, 0);
    -    }
    -    if (!bio) {
    -        return avs_errno(AVS_ENOMEM);
    -    }
    -    SSL_set_bio(sock->ssl, bio, bio);
    +    BIO *bio = NULL;
    +    if (sock->backend_type == AVS_NET_UDP_SOCKET) {
    +        bio = BIO_new_dgram(*(const int *) fd_ptr, 0);
    +        BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &peername.addr);
    +        DTLS_set_timer_cb(sock->ssl, dtls_timer_cb);
    +    } else {
    +        bio = BIO_new_socket(*(const int *) fd_ptr, 0);
    +    }
    +    if (!bio) {
    +        return avs_errno(AVS_ENOMEM);
    +    }
    +    SSL_set_bio(sock->ssl, bio, bio);
     
    -    if (sock->session_resumption_buffer) {
    -        const unsigned char *ptr =
    -                (const unsigned char *) sock->session_resumption_buffer;
    -        SSL_SESSION *session =
    -                d2i_SSL_SESSION(NULL, &ptr,
    -                                sock->session_resumption_buffer_size);
    -        if (session) {
    -            SSL_set_session(sock->ssl, session);
    -            SSL_SESSION_free(session);
    -        }
    -    }
    +    if (sock->session_resumption_buffer) {
    +        const unsigned char *ptr =
    +                (const unsigned char *) sock->session_resumption_buffer;
    +        SSL_SESSION *session =
    +                d2i_SSL_SESSION(NULL, &ptr,
    +                                sock->session_resumption_buffer_size);
    +        if (session) {
    +            SSL_set_session(sock->ssl, session);
    +            SSL_SESSION_free(session);
    +        }
    +    }
     
    -    if (SSL_connect(sock->ssl) <= 0) {
    -        return avs_errno(AVS_EPROTO);
    -    }
    -    return AVS_OK;
    -}
    +    if (SSL_connect(sock->ssl) <= 0) {
    +        return avs_errno(AVS_EPROTO);
    +    }
    +    return AVS_OK;
    +}
     
    @@ -551,52 +548,52 @@

    8.4.1.7.6. <

    It is thus necessary to modify the tls_receive() function to handle these differences appropriately:

    -
    static avs_error_t tls_receive(avs_net_socket_t *sock_,
    -                               size_t *out_bytes_received,
    -                               void *buffer,
    -                               size_t buffer_length) {
    -    tls_socket_impl_t *sock = (tls_socket_impl_t *) sock_;
    -    int pending = 0;
    -    if (sock->backend_type == AVS_NET_TCP_SOCKET) {
    -        pending = SSL_pending(sock->ssl);
    -    }
    -    if (pending > 0) {
    -        buffer_length = AVS_MIN(buffer_length, (size_t) pending);
    -    } else {
    -        const void *fd_ptr = avs_net_socket_get_system(sock->backend_socket);
    -        avs_net_socket_opt_value_t timeout;
    -        if (!fd_ptr
    -                || avs_is_err(avs_net_socket_get_opt(
    -                           sock->backend_socket,
    -                           AVS_NET_SOCKET_OPT_RECV_TIMEOUT, &timeout))) {
    -            return avs_errno(AVS_EBADF);
    -        }
    -        struct pollfd pfd = {
    -            .fd = *(const int *) fd_ptr,
    -            .events = POLLIN
    -        };
    -        int64_t timeout_ms;
    -        if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
    -                                        timeout.recv_timeout)) {
    -            timeout_ms = -1;
    -        } else if (timeout_ms < 0) {
    -            timeout_ms = 0;
    -        }
    -        if (poll(&pfd, 1, (int) timeout_ms) == 0) {
    -            return avs_errno(AVS_ETIMEDOUT);
    -        }
    -    }
    -    int bytes_received = SSL_read(sock->ssl, buffer, (int) buffer_length);
    -    if (bytes_received < 0) {
    -        return avs_errno(AVS_EPROTO);
    -    }
    -    *out_bytes_received = (size_t) bytes_received;
    -    if (sock->backend_type == AVS_NET_UDP_SOCKET && buffer_length > 0
    -            && (size_t) bytes_received == buffer_length) {
    -        return avs_errno(AVS_EMSGSIZE);
    -    }
    -    return AVS_OK;
    -}
    +
    static avs_error_t tls_receive(avs_net_socket_t *sock_,
    +                               size_t *out_bytes_received,
    +                               void *buffer,
    +                               size_t buffer_length) {
    +    tls_socket_impl_t *sock = (tls_socket_impl_t *) sock_;
    +    int pending = 0;
    +    if (sock->backend_type == AVS_NET_TCP_SOCKET) {
    +        pending = SSL_pending(sock->ssl);
    +    }
    +    if (pending > 0) {
    +        buffer_length = AVS_MIN(buffer_length, (size_t) pending);
    +    } else {
    +        const void *fd_ptr = avs_net_socket_get_system(sock->backend_socket);
    +        avs_net_socket_opt_value_t timeout;
    +        if (!fd_ptr
    +                || avs_is_err(avs_net_socket_get_opt(
    +                           sock->backend_socket,
    +                           AVS_NET_SOCKET_OPT_RECV_TIMEOUT, &timeout))) {
    +            return avs_errno(AVS_EBADF);
    +        }
    +        struct pollfd pfd = {
    +            .fd = *(const int *) fd_ptr,
    +            .events = POLLIN
    +        };
    +        int64_t timeout_ms;
    +        if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
    +                                        timeout.recv_timeout)) {
    +            timeout_ms = -1;
    +        } else if (timeout_ms < 0) {
    +            timeout_ms = 0;
    +        }
    +        if (poll(&pfd, 1, (int) timeout_ms) == 0) {
    +            return avs_errno(AVS_ETIMEDOUT);
    +        }
    +    }
    +    int bytes_received = SSL_read(sock->ssl, buffer, (int) buffer_length);
    +    if (bytes_received < 0) {
    +        return avs_errno(AVS_EPROTO);
    +    }
    +    *out_bytes_received = (size_t) bytes_received;
    +    if (sock->backend_type == AVS_NET_UDP_SOCKET && buffer_length > 0
    +            && (size_t) bytes_received == buffer_length) {
    +        return avs_errno(AVS_EMSGSIZE);
    +    }
    +    return AVS_OK;
    +}
     
    diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI.html index a1e09351..822d40ef 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI.html @@ -4,13 +4,10 @@ - 8.3. Networking API — Anjay 3.4.0 documentation + 8.3. Networking API — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
    - 3.4.0 + 3.4.1
    @@ -84,9 +81,9 @@
    @@ -143,8 +140,8 @@

    8.3.2. List of functions to implement

    Provide an implementation for:

    • _avs_net_create_udp_socket - a function with following signature:

      -
      avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket,
      -                                       const void *socket_configuration);
      +
      avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket,
      +                                       const void *socket_configuration);
       

      socket_configuration argument is a pointer to @@ -189,8 +186,8 @@

      8.3.2. List of functions to implementreturn avs_errno(AVS_ENOTSUP);.

      Function signature:

      -
      avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket,
      -                                       const void *socket_configuration);
      +
      avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket,
      +                                       const void *socket_configuration);
       

      socket_configuration argument is a pointer to @@ -204,7 +201,7 @@

      8.3.2. List of functions to implement
    • _avs_net_initialize_global_compat_state - a function with following signature:

      -
      avs_error_t _avs_net_initialize_global_compat_state(void);
      +
      avs_error_t _avs_net_initialize_global_compat_state(void);
       

      The function should return AVS_OK on success and an error code on error. @@ -214,7 +211,7 @@

      8.3.2. List of functions to implement
    • _avs_net_cleanup_global_compat_state - a function with following signature:

      -
      void _avs_net_cleanup_global_compat_state(void);
      +
      void _avs_net_cleanup_global_compat_state(void);
       

      The function should clean up any global state that is kept by the network @@ -224,12 +221,12 @@

      8.3.2. List of functions to implement

      avs_net_resolved_endpoint_get_host_port - a function declared in avs_addrinfo.h with the following signature:

      -
      avs_error_t
      -avs_net_resolved_endpoint_get_host_port(const avs_net_resolved_endpoint_t *endp,
      -                                        char *host,
      -                                        size_t hostlen,
      -                                        char *serv,
      -                                        size_t servlen);
      +
      avs_error_t
      +avs_net_resolved_endpoint_get_host_port(const avs_net_resolved_endpoint_t *endp,
      +                                        char *host,
      +                                        size_t hostlen,
      +                                        char *serv,
      +                                        size_t servlen);
       

      This function is used by the procedure that keeps the remote IP address diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Bind.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Bind.html index c8fe39f6..c2eb176c 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Bind.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Bind.html @@ -4,13 +4,10 @@ - 8.3.1.3. Bind operation — Anjay 3.4.0 documentation + 8.3.1.3. Bind operation — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -128,38 +125,38 @@

      8.3.1.3.2. <

      Implementation of the bind function is very similar to the previously implemented Connect one. Important changes are highlighted.

      -
      static avs_error_t
      -net_bind(avs_net_socket_t *sock_, const char *address, const char *port) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct addrinfo hints = {
      -        .ai_flags = AI_PASSIVE,
      -        .ai_socktype = sock->socktype
      -    };
      -    if (sock->fd >= 0) {
      -        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      -                   &(socklen_t) { sizeof(hints.ai_family) });
      -    }
      -    struct addrinfo *addr = NULL;
      -    avs_error_t err = AVS_OK;
      -    if (getaddrinfo(address, port, &hints, &addr) || !addr) {
      -        err = avs_errno(AVS_EADDRNOTAVAIL);
      -    } else if ((sock->fd < 0
      -                && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      -                                      addr->ai_protocol))
      -                           < 0)
      -               || setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 },
      -                             sizeof(int))) {
      -        err = avs_errno(AVS_UNKNOWN_ERROR);
      -    } else if (bind(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      -        err = avs_errno(AVS_ECONNREFUSED);
      -    }
      -    if (avs_is_err(err) && sock->fd >= 0) {
      -        close(sock->fd);
      -        sock->fd = -1;
      -    }
      -    freeaddrinfo(addr);
      -    return err;
      -}
      +
      static avs_error_t
      +net_bind(avs_net_socket_t *sock_, const char *address, const char *port) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct addrinfo hints = {
      +        .ai_flags = AI_PASSIVE,
      +        .ai_socktype = sock->socktype
      +    };
      +    if (sock->fd >= 0) {
      +        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      +                   &(socklen_t) { sizeof(hints.ai_family) });
      +    }
      +    struct addrinfo *addr = NULL;
      +    avs_error_t err = AVS_OK;
      +    if (getaddrinfo(address, port, &hints, &addr) || !addr) {
      +        err = avs_errno(AVS_EADDRNOTAVAIL);
      +    } else if ((sock->fd < 0
      +                && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      +                                      addr->ai_protocol))
      +                           < 0)
      +               || setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 },
      +                             sizeof(int))) {
      +        err = avs_errno(AVS_UNKNOWN_ERROR);
      +    } else if (bind(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      +        err = avs_errno(AVS_ECONNREFUSED);
      +    }
      +    if (avs_is_err(err) && sock->fd >= 0) {
      +        close(sock->fd);
      +        sock->fd = -1;
      +    }
      +    freeaddrinfo(addr);
      +    return err;
      +}
       

      This time getaddrinfo() is called with AI_PASSIVE flag to allow wildcard @@ -186,40 +183,40 @@

      8.3.1.3.2. <

      8.3.1.3.3. Changes to net_get_opt()

      Changes to this function are highlighted:

      -
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      -                               avs_net_socket_opt_key_t option_key,
      -                               avs_net_socket_opt_value_t *out_option_value) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    switch (option_key) {
      -    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      -        out_option_value->recv_timeout = sock->recv_timeout;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_STATE:
      -        if (sock->fd < 0) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      -        } else {
      -            sockaddr_union_t addr;
      -            if (!getpeername(sock->fd, &addr.addr,
      -                             &(socklen_t) { sizeof(addr) })
      -                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      -                        || (addr.in6.sin6_family == AF_INET6
      -                            && addr.in6.sin6_port != 0))) {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      -            } else {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      -            }
      -        }
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_INNER_MTU:
      -        out_option_value->mtu = 1464;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      -        out_option_value->flag = false;
      -        return AVS_OK;
      -    default:
      -        return avs_errno(AVS_ENOTSUP);
      -    }
      -}
      +
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      +                               avs_net_socket_opt_key_t option_key,
      +                               avs_net_socket_opt_value_t *out_option_value) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    switch (option_key) {
      +    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      +        out_option_value->recv_timeout = sock->recv_timeout;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_STATE:
      +        if (sock->fd < 0) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      +        } else {
      +            sockaddr_union_t addr;
      +            if (!getpeername(sock->fd, &addr.addr,
      +                             &(socklen_t) { sizeof(addr) })
      +                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      +                        || (addr.in6.sin6_family == AF_INET6
      +                            && addr.in6.sin6_port != 0))) {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      +            } else {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      +            }
      +        }
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_INNER_MTU:
      +        out_option_value->mtu = 1464;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      +        out_option_value->flag = false;
      +        return AVS_OK;
      +    default:
      +        return avs_errno(AVS_ENOTSUP);
      +    }
      +}
       

      The original variant assumed that if the socket descriptor was present, it is @@ -239,16 +236,16 @@

      8.3.1.3.4. <

      Its implementation mirrors the Get remote port operation from the previous tutorial, only with getsockname() used instead of getpeername():

      -
      static avs_error_t net_local_port(avs_net_socket_t *sock_,
      -                                  char *out_buffer,
      -                                  size_t out_buffer_size) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    sockaddr_union_t addr;
      -    if (getsockname(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      -        return avs_errno(AVS_UNKNOWN_ERROR);
      -    }
      -    return stringify_sockaddr_port(&addr, out_buffer, out_buffer_size);
      -}
      +
      static avs_error_t net_local_port(avs_net_socket_t *sock_,
      +                                  char *out_buffer,
      +                                  size_t out_buffer_size) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    sockaddr_union_t addr;
      +    if (getsockname(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      +        return avs_errno(AVS_UNKNOWN_ERROR);
      +    }
      +    return stringify_sockaddr_port(&addr, out_buffer, out_buffer_size);
      +}
       

      @@ -256,20 +253,20 @@

      8.3.1.3.4. <

      8.3.1.3.5. Update to vtable

      Of course the newly implemented functions need to be referenced in the virtual method table:

      -
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      -    .connect = net_connect,
      -    .send = net_send,
      -    .receive = net_receive,
      -    .bind = net_bind,
      -    .close = net_close,
      -    .cleanup = net_cleanup,
      -    .get_system_socket = net_system_socket,
      -    .get_remote_host = net_remote_host,
      -    .get_remote_port = net_remote_port,
      -    .get_local_port = net_local_port,
      -    .get_opt = net_get_opt,
      -    .set_opt = net_set_opt
      -};
      +
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      +    .connect = net_connect,
      +    .send = net_send,
      +    .receive = net_receive,
      +    .bind = net_bind,
      +    .close = net_close,
      +    .cleanup = net_cleanup,
      +    .get_system_socket = net_system_socket,
      +    .get_remote_host = net_remote_host,
      +    .get_remote_port = net_remote_port,
      +    .get_local_port = net_local_port,
      +    .get_opt = net_get_opt,
      +    .set_opt = net_set_opt
      +};
       
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-EventLoopSupport.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-EventLoopSupport.html index be4dabfc..b78cc375 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-EventLoopSupport.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-EventLoopSupport.html @@ -4,13 +4,10 @@ - 8.3.1.7. Event loop support — Anjay 3.4.0 documentation + 8.3.1.7. Event loop support — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -187,9 +184,9 @@

      8.3.1.7.3. <

      The header shall contain the necessary #include directives and declarations so that the requirements described above are met.

      For example, a POSIX compatibility header for Zephyr may look like:

      -
      #include <net/socket.h>
      +
      #include <net/socket.h>
       
      -typedef int sockfd_t;
      +typedef int sockfd_t;
       
       #ifndef pollfd
       #    define pollfd zsock_pollfd
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-IpStickiness.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-IpStickiness.html
      index 76aa8c82..d39872ad 100644
      --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-IpStickiness.html
      +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-IpStickiness.html
      @@ -4,13 +4,10 @@
         
       
         
      -  8.3.1.6. IP address stickiness support — Anjay 3.4.0 documentation
      +  8.3.1.6. IP address stickiness support — Anjay 3.4.1 documentation
             
             
             
      -  
         
               
               
      @@ -32,7 +29,7 @@
                   
                 
                     
      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -155,40 +152,40 @@

      8.3.1.6.2. <

      8.3.1.6.3. Initialization

      -
      typedef struct {
      -    const avs_net_socket_v_table_t *operations;
      -    int socktype;
      -    int fd;
      -    avs_time_duration_t recv_timeout;
      -    char remote_hostname[256];
      -    bool shut_down;
      -    size_t bytes_sent;
      -    size_t bytes_received;
      -    avs_net_resolved_endpoint_t *preferred_endpoint;
      -} net_socket_impl_t;
      +
      typedef struct {
      +    const avs_net_socket_v_table_t *operations;
      +    int socktype;
      +    int fd;
      +    avs_time_duration_t recv_timeout;
      +    char remote_hostname[256];
      +    bool shut_down;
      +    size_t bytes_sent;
      +    size_t bytes_received;
      +    avs_net_resolved_endpoint_t *preferred_endpoint;
      +} net_socket_impl_t;
       
       // ...
       
      -static avs_error_t
      -net_create_socket(avs_net_socket_t **socket_ptr,
      -                  const avs_net_socket_configuration_t *configuration,
      -                  int socktype) {
      -    assert(socket_ptr);
      -    assert(!*socket_ptr);
      -    (void) configuration;
      -    net_socket_impl_t *socket =
      -            (net_socket_impl_t *) avs_calloc(1, sizeof(net_socket_impl_t));
      -    if (!socket) {
      -        return avs_errno(AVS_ENOMEM);
      -    }
      -    socket->operations = &NET_SOCKET_VTABLE;
      -    socket->socktype = socktype;
      -    socket->fd = -1;
      -    socket->recv_timeout = avs_time_duration_from_scalar(30, AVS_TIME_S);
      -    socket->preferred_endpoint = configuration->preferred_endpoint;
      -    *socket_ptr = (avs_net_socket_t *) socket;
      -    return AVS_OK;
      -}
      +static avs_error_t
      +net_create_socket(avs_net_socket_t **socket_ptr,
      +                  const avs_net_socket_configuration_t *configuration,
      +                  int socktype) {
      +    assert(socket_ptr);
      +    assert(!*socket_ptr);
      +    (void) configuration;
      +    net_socket_impl_t *socket =
      +            (net_socket_impl_t *) avs_calloc(1, sizeof(net_socket_impl_t));
      +    if (!socket) {
      +        return avs_errno(AVS_ENOMEM);
      +    }
      +    socket->operations = &NET_SOCKET_VTABLE;
      +    socket->socktype = socktype;
      +    socket->fd = -1;
      +    socket->recv_timeout = avs_time_duration_from_scalar(30, AVS_TIME_S);
      +    socket->preferred_endpoint = configuration->preferred_endpoint;
      +    *socket_ptr = (avs_net_socket_t *) socket;
      +    return AVS_OK;
      +}
       

      The preferred_endpoint field is intended as a pointer into user-allocated @@ -201,62 +198,62 @@

      8.3.1.6.4. <

      In addition to the highlighted changes, the original addr variable has been renamed to addrs. This change has not been highlighted for clarity.

      -
      static avs_error_t
      -net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct addrinfo hints = {
      -        .ai_socktype = sock->socktype
      -    };
      -    if (sock->fd >= 0) {
      -        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      -                   &(socklen_t) { sizeof(hints.ai_family) });
      -    }
      -    struct addrinfo *addrs = NULL;
      -    avs_error_t err = AVS_OK;
      -    if (getaddrinfo(host, port, &hints, &addrs) || !addrs) {
      -        err = avs_errno(AVS_EADDRNOTAVAIL);
      -    } else if (sock->fd < 0
      -               && (sock->fd = socket(addrs->ai_family, addrs->ai_socktype,
      -                                     addrs->ai_protocol))
      -                          < 0) {
      -        err = avs_errno(AVS_UNKNOWN_ERROR);
      -    } else {
      -        const struct addrinfo *addr = addrs;
      -        if (sock->preferred_endpoint
      -                && sock->preferred_endpoint->size == sizeof(sockaddr_union_t)) {
      -            while (addr) {
      -                if (addr->ai_addrlen <= sizeof(sockaddr_union_t)
      -                        && memcmp(addr->ai_addr,
      -                                  sock->preferred_endpoint->data.buf,
      -                                  addr->ai_addrlen)
      -                                       == 0) {
      -                    break;
      -                }
      -                addr = addr->ai_next;
      -            }
      -        }
      -        if (!addr) {
      +
      static avs_error_t
      +net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct addrinfo hints = {
      +        .ai_socktype = sock->socktype
      +    };
      +    if (sock->fd >= 0) {
      +        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      +                   &(socklen_t) { sizeof(hints.ai_family) });
      +    }
      +    struct addrinfo *addrs = NULL;
      +    avs_error_t err = AVS_OK;
      +    if (getaddrinfo(host, port, &hints, &addrs) || !addrs) {
      +        err = avs_errno(AVS_EADDRNOTAVAIL);
      +    } else if (sock->fd < 0
      +               && (sock->fd = socket(addrs->ai_family, addrs->ai_socktype,
      +                                     addrs->ai_protocol))
      +                          < 0) {
      +        err = avs_errno(AVS_UNKNOWN_ERROR);
      +    } else {
      +        const struct addrinfo *addr = addrs;
      +        if (sock->preferred_endpoint
      +                && sock->preferred_endpoint->size == sizeof(sockaddr_union_t)) {
      +            while (addr) {
      +                if (addr->ai_addrlen <= sizeof(sockaddr_union_t)
      +                        && memcmp(addr->ai_addr,
      +                                  sock->preferred_endpoint->data.buf,
      +                                  addr->ai_addrlen)
      +                                       == 0) {
      +                    break;
      +                }
      +                addr = addr->ai_next;
      +            }
      +        }
      +        if (!addr) {
                   // Preferred endpoint not found, use the first one
      -            addr = addrs;
      -        }
      -        if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      -            err = avs_errno(AVS_ECONNREFUSED);
      -        }
      -        if (sock->preferred_endpoint && avs_is_ok(err)) {
      -            assert(addr->ai_addrlen <= sizeof(sockaddr_union_t));
      -            memcpy(sock->preferred_endpoint->data.buf, addr->ai_addr,
      -                   addr->ai_addrlen);
      -            sock->preferred_endpoint->size = sizeof(sockaddr_union_t);
      -        }
      -    }
      -    if (avs_is_ok(err)) {
      -        sock->shut_down = false;
      -        snprintf(sock->remote_hostname, sizeof(sock->remote_hostname), "%s",
      -                 host);
      -    }
      -    freeaddrinfo(addrs);
      -    return err;
      -}
      +            addr = addrs;
      +        }
      +        if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      +            err = avs_errno(AVS_ECONNREFUSED);
      +        }
      +        if (sock->preferred_endpoint && avs_is_ok(err)) {
      +            assert(addr->ai_addrlen <= sizeof(sockaddr_union_t));
      +            memcpy(sock->preferred_endpoint->data.buf, addr->ai_addr,
      +                   addr->ai_addrlen);
      +            sock->preferred_endpoint->size = sizeof(sockaddr_union_t);
      +        }
      +    }
      +    if (avs_is_ok(err)) {
      +        sock->shut_down = false;
      +        snprintf(sock->remote_hostname, sizeof(sock->remote_hostname), "%s",
      +                 host);
      +    }
      +    freeaddrinfo(addrs);
      +    return err;
      +}
       

      In the code before the connect() call, if the preferred_endpoint pointer @@ -269,27 +266,27 @@

      8.3.1.6.4. <

      8.3.1.6.5. avs_net_resolved_endpoint_get_host_port()

      -
      avs_error_t
      -avs_net_resolved_endpoint_get_host_port(const avs_net_resolved_endpoint_t *endp,
      -                                        char *host,
      -                                        size_t hostlen,
      -                                        char *serv,
      -                                        size_t servlen) {
      -    AVS_STATIC_ASSERT(sizeof(endp->data.buf) >= sizeof(sockaddr_union_t),
      -                      data_buffer_big_enough);
      -    if (endp->size != sizeof(sockaddr_union_t)) {
      -        return avs_errno(AVS_EINVAL);
      -    }
      -    const sockaddr_union_t *addr = (const sockaddr_union_t *) &endp->data.buf;
      -    avs_error_t err = AVS_OK;
      -    (void) ((host
      -             && avs_is_err(
      -                        (err = stringify_sockaddr_host(addr, host, hostlen))))
      -            || (serv
      -                && avs_is_err((err = stringify_sockaddr_port(addr, serv,
      -                                                             servlen)))));
      -    return err;
      -}
      +
      avs_error_t
      +avs_net_resolved_endpoint_get_host_port(const avs_net_resolved_endpoint_t *endp,
      +                                        char *host,
      +                                        size_t hostlen,
      +                                        char *serv,
      +                                        size_t servlen) {
      +    AVS_STATIC_ASSERT(sizeof(endp->data.buf) >= sizeof(sockaddr_union_t),
      +                      data_buffer_big_enough);
      +    if (endp->size != sizeof(sockaddr_union_t)) {
      +        return avs_errno(AVS_EINVAL);
      +    }
      +    const sockaddr_union_t *addr = (const sockaddr_union_t *) &endp->data.buf;
      +    avs_error_t err = AVS_OK;
      +    (void) ((host
      +             && avs_is_err(
      +                        (err = stringify_sockaddr_host(addr, host, hostlen))))
      +            || (serv
      +                && avs_is_err((err = stringify_sockaddr_port(addr, serv,
      +                                                             servlen)))));
      +    return err;
      +}
       

      Since in our implementation avs_net_resolved_endpoint_t is just a wrapper diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Minimal.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Minimal.html index b706cbb2..367ffcb7 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Minimal.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Minimal.html @@ -4,13 +4,10 @@ - 8.3.1.1. Minimal socket implementation — Anjay 3.4.0 documentation + 8.3.1.1. Minimal socket implementation — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -186,22 +183,22 @@

      8.3.1.1.3. < that can be included to provide forward declarations of them. Hence, we start with manually including the forward declarations, as quoted in the previous article:

      -
      avs_error_t _avs_net_initialize_global_compat_state(void);
      -void _avs_net_cleanup_global_compat_state(void);
      -avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket,
      -                                       const void *socket_configuration);
      -avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket,
      -                                       const void *socket_configuration);
      +
      avs_error_t _avs_net_initialize_global_compat_state(void);
      +void _avs_net_cleanup_global_compat_state(void);
      +avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket,
      +                                       const void *socket_configuration);
      +avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket,
      +                                       const void *socket_configuration);
       

      We actually won’t need any global state for our implementation, so implementing the _avs_net_{initialize,cleanup}_global_compat_state() functions is trivial:

      -
      avs_error_t _avs_net_initialize_global_compat_state(void) {
      -    return AVS_OK;
      -}
      +
      avs_error_t _avs_net_initialize_global_compat_state(void) {
      +    return AVS_OK;
      +}
       
      -void _avs_net_cleanup_global_compat_state(void) {}
      +void _avs_net_cleanup_global_compat_state(void) {}
       

      Global state may be useful on some platforms where using the network requires @@ -222,60 +219,60 @@

      8.3.1.1.3. < that always returns an error code.

      With BSD-style socket API, however, it is actually trivial to support both TCP and UDP sockets, so we will do just that.

      -
      typedef struct {
      -    const avs_net_socket_v_table_t *operations;
      -    int socktype;
      -    int fd;
      -    avs_time_duration_t recv_timeout;
      -} net_socket_impl_t;
      +
      typedef struct {
      +    const avs_net_socket_v_table_t *operations;
      +    int socktype;
      +    int fd;
      +    avs_time_duration_t recv_timeout;
      +} net_socket_impl_t;
       
       // ... implementations of NET_SOCKET_VTABLE functions go here
       // ... they will be discussed separately later
       
      -static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      -    .connect = net_connect,
      -    .send = net_send,
      -    .receive = net_receive,
      -    .close = net_close,
      -    .cleanup = net_cleanup,
      -    .get_system_socket = net_system_socket,
      -    .get_opt = net_get_opt,
      -    .set_opt = net_set_opt
      -};
      +static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      +    .connect = net_connect,
      +    .send = net_send,
      +    .receive = net_receive,
      +    .close = net_close,
      +    .cleanup = net_cleanup,
      +    .get_system_socket = net_system_socket,
      +    .get_opt = net_get_opt,
      +    .set_opt = net_set_opt
      +};
       
      -static avs_error_t
      -net_create_socket(avs_net_socket_t **socket_ptr,
      -                  const avs_net_socket_configuration_t *configuration,
      -                  int socktype) {
      -    assert(socket_ptr);
      -    assert(!*socket_ptr);
      -    (void) configuration;
      -    net_socket_impl_t *socket =
      -            (net_socket_impl_t *) avs_calloc(1, sizeof(net_socket_impl_t));
      -    if (!socket) {
      -        return avs_errno(AVS_ENOMEM);
      -    }
      -    socket->operations = &NET_SOCKET_VTABLE;
      -    socket->socktype = socktype;
      -    socket->fd = -1;
      -    socket->recv_timeout = avs_time_duration_from_scalar(30, AVS_TIME_S);
      -    *socket_ptr = (avs_net_socket_t *) socket;
      -    return AVS_OK;
      -}
      +static avs_error_t
      +net_create_socket(avs_net_socket_t **socket_ptr,
      +                  const avs_net_socket_configuration_t *configuration,
      +                  int socktype) {
      +    assert(socket_ptr);
      +    assert(!*socket_ptr);
      +    (void) configuration;
      +    net_socket_impl_t *socket =
      +            (net_socket_impl_t *) avs_calloc(1, sizeof(net_socket_impl_t));
      +    if (!socket) {
      +        return avs_errno(AVS_ENOMEM);
      +    }
      +    socket->operations = &NET_SOCKET_VTABLE;
      +    socket->socktype = socktype;
      +    socket->fd = -1;
      +    socket->recv_timeout = avs_time_duration_from_scalar(30, AVS_TIME_S);
      +    *socket_ptr = (avs_net_socket_t *) socket;
      +    return AVS_OK;
      +}
       
      -avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket_ptr,
      -                                       const void *configuration) {
      -    return net_create_socket(
      -            socket_ptr, (const avs_net_socket_configuration_t *) configuration,
      -            SOCK_DGRAM);
      -}
      +avs_error_t _avs_net_create_udp_socket(avs_net_socket_t **socket_ptr,
      +                                       const void *configuration) {
      +    return net_create_socket(
      +            socket_ptr, (const avs_net_socket_configuration_t *) configuration,
      +            SOCK_DGRAM);
      +}
       
      -avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket_ptr,
      -                                       const void *configuration) {
      -    return net_create_socket(
      -            socket_ptr, (const avs_net_socket_configuration_t *) configuration,
      -            SOCK_STREAM);
      -}
      +avs_error_t _avs_net_create_tcp_socket(avs_net_socket_t **socket_ptr,
      +                                       const void *configuration) {
      +    return net_create_socket(
      +            socket_ptr, (const avs_net_socket_configuration_t *) configuration,
      +            SOCK_STREAM);
      +}
       

      avs_commons uses an object-oriented paradigm for its socket layer. Any @@ -309,31 +306,31 @@

      8.3.1.1.3. <

      8.3.1.1.5. Implementing socket methods

      8.3.1.1.5.1. Connect

      -
      static avs_error_t
      -net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct addrinfo hints = {
      -        .ai_socktype = sock->socktype
      -    };
      -    if (sock->fd >= 0) {
      -        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      -                   &(socklen_t) { sizeof(hints.ai_family) });
      -    }
      -    struct addrinfo *addr = NULL;
      -    avs_error_t err = AVS_OK;
      -    if (getaddrinfo(host, port, &hints, &addr) || !addr) {
      -        err = avs_errno(AVS_EADDRNOTAVAIL);
      -    } else if (sock->fd < 0
      -               && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      -                                     addr->ai_protocol))
      -                          < 0) {
      -        err = avs_errno(AVS_UNKNOWN_ERROR);
      -    } else if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      -        err = avs_errno(AVS_ECONNREFUSED);
      -    }
      -    freeaddrinfo(addr);
      -    return err;
      -}
      +
      static avs_error_t
      +net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct addrinfo hints = {
      +        .ai_socktype = sock->socktype
      +    };
      +    if (sock->fd >= 0) {
      +        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      +                   &(socklen_t) { sizeof(hints.ai_family) });
      +    }
      +    struct addrinfo *addr = NULL;
      +    avs_error_t err = AVS_OK;
      +    if (getaddrinfo(host, port, &hints, &addr) || !addr) {
      +        err = avs_errno(AVS_EADDRNOTAVAIL);
      +    } else if (sock->fd < 0
      +               && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      +                                     addr->ai_protocol))
      +                          < 0) {
      +        err = avs_errno(AVS_UNKNOWN_ERROR);
      +    } else if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      +        err = avs_errno(AVS_ECONNREFUSED);
      +    }
      +    freeaddrinfo(addr);
      +    return err;
      +}
       

      In each of the vtable methods, the first avs_net_socket_t * argument is the @@ -377,15 +374,15 @@

      8.3.1.1.5. <

      8.3.1.1.5.2. Send

      The send() implementation is self-explanatory:

      -
      static avs_error_t
      -net_send(avs_net_socket_t *sock_, const void *buffer, size_t buffer_length) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    ssize_t written = send(sock->fd, buffer, buffer_length, MSG_NOSIGNAL);
      -    if (written >= 0 && (size_t) written == buffer_length) {
      -        return AVS_OK;
      -    }
      -    return avs_errno(AVS_EIO);
      -}
      +
      static avs_error_t
      +net_send(avs_net_socket_t *sock_, const void *buffer, size_t buffer_length) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    ssize_t written = send(sock->fd, buffer, buffer_length, MSG_NOSIGNAL);
      +    if (written >= 0 && (size_t) written == buffer_length) {
      +        return AVS_OK;
      +    }
      +    return avs_errno(AVS_EIO);
      +}
       
      @@ -406,36 +403,36 @@

      8.3.1.1.5.2.

      8.3.1.1.5.3. Receive

      -
      static avs_error_t net_receive(avs_net_socket_t *sock_,
      -                               size_t *out_bytes_received,
      -                               void *buffer,
      -                               size_t buffer_length) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct pollfd pfd = {
      -        .fd = sock->fd,
      -        .events = POLLIN
      -    };
      -    int64_t timeout_ms;
      -    if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
      -                                    sock->recv_timeout)) {
      -        timeout_ms = -1;
      -    } else if (timeout_ms < 0) {
      -        timeout_ms = 0;
      -    }
      -    if (poll(&pfd, 1, (int) timeout_ms) == 0) {
      -        return avs_errno(AVS_ETIMEDOUT);
      -    }
      -    ssize_t bytes_received = read(sock->fd, buffer, buffer_length);
      -    if (bytes_received < 0) {
      -        return avs_errno(AVS_EIO);
      -    }
      -    *out_bytes_received = (size_t) bytes_received;
      -    if (buffer_length > 0 && sock->socktype == SOCK_DGRAM
      -            && (size_t) bytes_received == buffer_length) {
      -        return avs_errno(AVS_EMSGSIZE);
      -    }
      -    return AVS_OK;
      -}
      +
      static avs_error_t net_receive(avs_net_socket_t *sock_,
      +                               size_t *out_bytes_received,
      +                               void *buffer,
      +                               size_t buffer_length) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct pollfd pfd = {
      +        .fd = sock->fd,
      +        .events = POLLIN
      +    };
      +    int64_t timeout_ms;
      +    if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
      +                                    sock->recv_timeout)) {
      +        timeout_ms = -1;
      +    } else if (timeout_ms < 0) {
      +        timeout_ms = 0;
      +    }
      +    if (poll(&pfd, 1, (int) timeout_ms) == 0) {
      +        return avs_errno(AVS_ETIMEDOUT);
      +    }
      +    ssize_t bytes_received = read(sock->fd, buffer, buffer_length);
      +    if (bytes_received < 0) {
      +        return avs_errno(AVS_EIO);
      +    }
      +    *out_bytes_received = (size_t) bytes_received;
      +    if (buffer_length > 0 && sock->socktype == SOCK_DGRAM
      +            && (size_t) bytes_received == buffer_length) {
      +        return avs_errno(AVS_EMSGSIZE);
      +    }
      +    return AVS_OK;
      +}
       

      Implementation of the receive method is a bit more complicated than that of the @@ -471,17 +468,17 @@

      8.3.1.1.5.2.

      8.3.1.1.5.4. Close

      -
      static avs_error_t net_close(avs_net_socket_t *sock_) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    avs_error_t err = AVS_OK;
      -    if (sock->fd >= 0) {
      -        if (close(sock->fd)) {
      -            err = avs_errno(AVS_EIO);
      -        }
      -        sock->fd = -1;
      -    }
      -    return err;
      -}
      +
      static avs_error_t net_close(avs_net_socket_t *sock_) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    avs_error_t err = AVS_OK;
      +    if (sock->fd >= 0) {
      +        if (close(sock->fd)) {
      +            err = avs_errno(AVS_EIO);
      +        }
      +        sock->fd = -1;
      +    }
      +    return err;
      +}
       

      This function is pretty self-explanatory - but please note that unlike the POSIX @@ -490,15 +487,15 @@

      8.3.1.1.5.4.

      8.3.1.1.5.5. Cleanup

      -
      static avs_error_t net_cleanup(avs_net_socket_t **sock_ptr) {
      -    avs_error_t err = AVS_OK;
      -    if (sock_ptr && *sock_ptr) {
      -        err = net_close(*sock_ptr);
      -        avs_free(*sock_ptr);
      -        *sock_ptr = NULL;
      -    }
      -    return err;
      -}
      +
      static avs_error_t net_cleanup(avs_net_socket_t **sock_ptr) {
      +    avs_error_t err = AVS_OK;
      +    if (sock_ptr && *sock_ptr) {
      +        err = net_close(*sock_ptr);
      +        avs_free(*sock_ptr);
      +        *sock_ptr = NULL;
      +    }
      +    return err;
      +}
       

      The cleanup operation is also self-explanatory, although please note that there @@ -507,10 +504,10 @@

      8.3.1.1.5.5

      8.3.1.1.5.6. Get system socket

      -
      static const void *net_system_socket(avs_net_socket_t *sock_) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    return &sock->fd;
      -}
      +
      static const void *net_system_socket(avs_net_socket_t *sock_) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    return &sock->fd;
      +}
       

      This function is only called by Anjay from anjay_event_loop_run() and @@ -526,44 +523,44 @@

      8.3.1.1.5.6

      8.3.1.1.5.7. Get/set socket options

      -
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      -                               avs_net_socket_opt_key_t option_key,
      -                               avs_net_socket_opt_value_t *out_option_value) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    switch (option_key) {
      -    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      -        out_option_value->recv_timeout = sock->recv_timeout;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_STATE:
      -        if (sock->fd < 0) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      -        } else {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      -        }
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_INNER_MTU:
      -        out_option_value->mtu = 1464;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      -        out_option_value->flag = false;
      -        return AVS_OK;
      -    default:
      -        return avs_errno(AVS_ENOTSUP);
      -    }
      -}
      +
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      +                               avs_net_socket_opt_key_t option_key,
      +                               avs_net_socket_opt_value_t *out_option_value) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    switch (option_key) {
      +    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      +        out_option_value->recv_timeout = sock->recv_timeout;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_STATE:
      +        if (sock->fd < 0) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      +        } else {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      +        }
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_INNER_MTU:
      +        out_option_value->mtu = 1464;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      +        out_option_value->flag = false;
      +        return AVS_OK;
      +    default:
      +        return avs_errno(AVS_ENOTSUP);
      +    }
      +}
       
      -static avs_error_t net_set_opt(avs_net_socket_t *sock_,
      -                               avs_net_socket_opt_key_t option_key,
      -                               avs_net_socket_opt_value_t option_value) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    switch (option_key) {
      -    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      -        sock->recv_timeout = option_value.recv_timeout;
      -        return AVS_OK;
      -    default:
      -        return avs_errno(AVS_ENOTSUP);
      -    }
      -}
      +static avs_error_t net_set_opt(avs_net_socket_t *sock_,
      +                               avs_net_socket_opt_key_t option_key,
      +                               avs_net_socket_opt_value_t option_value) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    switch (option_key) {
      +    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      +        sock->recv_timeout = option_value.recv_timeout;
      +        return AVS_OK;
      +    default:
      +        return avs_errno(AVS_ENOTSUP);
      +    }
      +}
       

      The get_opt/set_opt interface is used for querying and setting various diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-OtherFeatures.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-OtherFeatures.html index 26fc0bbe..a9cdea24 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-OtherFeatures.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-OtherFeatures.html @@ -4,13 +4,10 @@ - 8.3.1.8. Other features — Anjay 3.4.0 documentation + 8.3.1.8. Other features — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@

      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-RemoteHostPort.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-RemoteHostPort.html index ae2d820c..b75b1abb 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-RemoteHostPort.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-RemoteHostPort.html @@ -4,13 +4,10 @@ - 8.3.1.2. Get remote host/port operations — Anjay 3.4.0 documentation + 8.3.1.2. Get remote host/port operations — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -126,45 +123,45 @@

      8.3.1.2.1. <

      8.3.1.2.2. Get remote host operation

      -
      #include <arpa/inet.h>
      +
      #include <arpa/inet.h>
       
       // ...
       
      -typedef union {
      -    struct sockaddr addr;
      -    struct sockaddr_in in;
      -    struct sockaddr_in6 in6;
      -    struct sockaddr_storage storage;
      -} sockaddr_union_t;
      +typedef union {
      +    struct sockaddr addr;
      +    struct sockaddr_in in;
      +    struct sockaddr_in6 in6;
      +    struct sockaddr_storage storage;
      +} sockaddr_union_t;
       
       // ...
       
      -static avs_error_t stringify_sockaddr_host(const sockaddr_union_t *addr,
      -                                           char *out_buffer,
      -                                           size_t out_buffer_size) {
      -    if ((addr->in.sin_family == AF_INET
      -         && inet_ntop(AF_INET, &addr->in.sin_addr, out_buffer,
      -                      (socklen_t) out_buffer_size))
      -            || (addr->in6.sin6_family == AF_INET6
      -                && inet_ntop(AF_INET6, &addr->in6.sin6_addr, out_buffer,
      -                             (socklen_t) out_buffer_size))) {
      -        return AVS_OK;
      -    }
      -    return avs_errno(AVS_UNKNOWN_ERROR);
      -}
      +static avs_error_t stringify_sockaddr_host(const sockaddr_union_t *addr,
      +                                           char *out_buffer,
      +                                           size_t out_buffer_size) {
      +    if ((addr->in.sin_family == AF_INET
      +         && inet_ntop(AF_INET, &addr->in.sin_addr, out_buffer,
      +                      (socklen_t) out_buffer_size))
      +            || (addr->in6.sin6_family == AF_INET6
      +                && inet_ntop(AF_INET6, &addr->in6.sin6_addr, out_buffer,
      +                             (socklen_t) out_buffer_size))) {
      +        return AVS_OK;
      +    }
      +    return avs_errno(AVS_UNKNOWN_ERROR);
      +}
       
       // ...
       
      -static avs_error_t net_remote_host(avs_net_socket_t *sock_,
      -                                   char *out_buffer,
      -                                   size_t out_buffer_size) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    sockaddr_union_t addr;
      -    if (getpeername(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      -        return avs_errno(AVS_UNKNOWN_ERROR);
      -    }
      -    return stringify_sockaddr_host(&addr, out_buffer, out_buffer_size);
      -}
      +static avs_error_t net_remote_host(avs_net_socket_t *sock_,
      +                                   char *out_buffer,
      +                                   size_t out_buffer_size) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    sockaddr_union_t addr;
      +    if (getpeername(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      +        return avs_errno(AVS_UNKNOWN_ERROR);
      +    }
      +    return stringify_sockaddr_host(&addr, out_buffer, out_buffer_size);
      +}
       

      The net_remote_host() function essentially wraps the POSIX getpeername() @@ -184,40 +181,40 @@

      8.3.1.2.1. <

      8.3.1.2.3. Get remote port operation

      -
      #include <inttypes.h>
      +
      #include <inttypes.h>
       // ...
      -#include <avsystem/commons/avs_utils.h>
      +#include <avsystem/commons/avs_utils.h>
       
       // ...
       
      -static avs_error_t stringify_sockaddr_port(const sockaddr_union_t *addr,
      -                                           char *out_buffer,
      -                                           size_t out_buffer_size) {
      -    if ((addr->in.sin_family == AF_INET
      -         && avs_simple_snprintf(out_buffer, out_buffer_size, "%" PRIu16,
      -                                ntohs(addr->in.sin_port))
      -                    >= 0)
      -            || (addr->in6.sin6_family == AF_INET6
      -                && avs_simple_snprintf(out_buffer, out_buffer_size, "%" PRIu16,
      -                                       ntohs(addr->in6.sin6_port))
      -                           >= 0)) {
      -        return AVS_OK;
      -    }
      -    return avs_errno(AVS_UNKNOWN_ERROR);
      -}
      +static avs_error_t stringify_sockaddr_port(const sockaddr_union_t *addr,
      +                                           char *out_buffer,
      +                                           size_t out_buffer_size) {
      +    if ((addr->in.sin_family == AF_INET
      +         && avs_simple_snprintf(out_buffer, out_buffer_size, "%" PRIu16,
      +                                ntohs(addr->in.sin_port))
      +                    >= 0)
      +            || (addr->in6.sin6_family == AF_INET6
      +                && avs_simple_snprintf(out_buffer, out_buffer_size, "%" PRIu16,
      +                                       ntohs(addr->in6.sin6_port))
      +                           >= 0)) {
      +        return AVS_OK;
      +    }
      +    return avs_errno(AVS_UNKNOWN_ERROR);
      +}
       
       // ...
       
      -static avs_error_t net_remote_port(avs_net_socket_t *sock_,
      -                                   char *out_buffer,
      -                                   size_t out_buffer_size) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    sockaddr_union_t addr;
      -    if (getpeername(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      -        return avs_errno(AVS_UNKNOWN_ERROR);
      -    }
      -    return stringify_sockaddr_port(&addr, out_buffer, out_buffer_size);
      -}
      +static avs_error_t net_remote_port(avs_net_socket_t *sock_,
      +                                   char *out_buffer,
      +                                   size_t out_buffer_size) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    sockaddr_union_t addr;
      +    if (getpeername(sock->fd, &addr.addr, &(socklen_t) { sizeof(addr) })) {
      +        return avs_errno(AVS_UNKNOWN_ERROR);
      +    }
      +    return stringify_sockaddr_port(&addr, out_buffer, out_buffer_size);
      +}
       

      Similar to net_remote_host(), this function also calls getpeername() - @@ -229,18 +226,18 @@

      8.3.1.2.1. <

      8.3.1.2.4. Update to vtable

      Of course the newly implemented function need to be referenced in the virtual method table:

      -
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      -    .connect = net_connect,
      -    .send = net_send,
      -    .receive = net_receive,
      -    .close = net_close,
      -    .cleanup = net_cleanup,
      -    .get_system_socket = net_system_socket,
      -    .get_remote_host = net_remote_host,
      -    .get_remote_port = net_remote_port,
      -    .get_opt = net_get_opt,
      -    .set_opt = net_set_opt
      -};
      +
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      +    .connect = net_connect,
      +    .send = net_send,
      +    .receive = net_receive,
      +    .close = net_close,
      +    .cleanup = net_cleanup,
      +    .get_system_socket = net_system_socket,
      +    .get_remote_host = net_remote_host,
      +    .get_remote_port = net_remote_port,
      +    .get_opt = net_get_opt,
      +    .set_opt = net_set_opt
      +};
       

      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-ShutdownRemoteHostname.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-ShutdownRemoteHostname.html index 2277ece0..9e9d5e15 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-ShutdownRemoteHostname.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-ShutdownRemoteHostname.html @@ -4,13 +4,10 @@ - 8.3.1.4. Remote hostname and shutdown operations — Anjay 3.4.0 documentation + 8.3.1.4. Remote hostname and shutdown operations — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -126,8 +123,8 @@

      8.3.1.4.1. < the “offline mode” functionality.

      8.3.1.4.1.1. Get remote hostname operation

      -
      typedef avs_error_t (*avs_net_socket_get_remote_hostname_t)(
      -        avs_net_socket_t *socket, char *out_buffer, size_t out_buffer_size);
      +
      typedef avs_error_t (*avs_net_socket_get_remote_hostname_t)(
      +        avs_net_socket_t *socket, char *out_buffer, size_t out_buffer_size);
       

      This operation is similar in concept to the previously introduced @@ -138,7 +135,7 @@

      8.3.1.4.1.1.

      8.3.1.4.1.2. Shutdown operation

      -
      typedef avs_error_t (*avs_net_socket_shutdown_t)(avs_net_socket_t *socket);
      +
      typedef avs_error_t (*avs_net_socket_shutdown_t)(avs_net_socket_t *socket);
       

      This API is intended as a parallel to the POSIX shutdown() function (called @@ -160,14 +157,14 @@

      8.3.1.4.2. < stored anywhere in our current logic; there is also no standard POSIX API to determine whether the socket is in the shut down state. That’s why we will need additional fields in our socket structure to implement these operations:

      -
      typedef struct {
      -    const avs_net_socket_v_table_t *operations;
      -    int socktype;
      -    int fd;
      -    avs_time_duration_t recv_timeout;
      -    char remote_hostname[256];
      -    bool shut_down;
      -} net_socket_impl_t;
      +
      typedef struct {
      +    const avs_net_socket_v_table_t *operations;
      +    int socktype;
      +    int fd;
      +    avs_time_duration_t recv_timeout;
      +    char remote_hostname[256];
      +    bool shut_down;
      +} net_socket_impl_t;
       

      The remote_hostname field will contain the last known hostname to which the @@ -181,36 +178,36 @@

      8.3.1.4.3. <

      The only place where there is direct access to the hostname, is the Connect function, so we need to update it accordingly to cache this information if the connection is successful:

      -
      static avs_error_t
      -net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct addrinfo hints = {
      -        .ai_socktype = sock->socktype
      -    };
      -    if (sock->fd >= 0) {
      -        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      -                   &(socklen_t) { sizeof(hints.ai_family) });
      -    }
      -    struct addrinfo *addr = NULL;
      -    avs_error_t err = AVS_OK;
      -    if (getaddrinfo(host, port, &hints, &addr) || !addr) {
      -        err = avs_errno(AVS_EADDRNOTAVAIL);
      -    } else if (sock->fd < 0
      -               && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      -                                     addr->ai_protocol))
      -                          < 0) {
      -        err = avs_errno(AVS_UNKNOWN_ERROR);
      -    } else if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      -        err = avs_errno(AVS_ECONNREFUSED);
      -    }
      -    if (avs_is_ok(err)) {
      -        sock->shut_down = false;
      -        snprintf(sock->remote_hostname, sizeof(sock->remote_hostname), "%s",
      -                 host);
      -    }
      -    freeaddrinfo(addr);
      -    return err;
      -}
      +
      static avs_error_t
      +net_connect(avs_net_socket_t *sock_, const char *host, const char *port) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct addrinfo hints = {
      +        .ai_socktype = sock->socktype
      +    };
      +    if (sock->fd >= 0) {
      +        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      +                   &(socklen_t) { sizeof(hints.ai_family) });
      +    }
      +    struct addrinfo *addr = NULL;
      +    avs_error_t err = AVS_OK;
      +    if (getaddrinfo(host, port, &hints, &addr) || !addr) {
      +        err = avs_errno(AVS_EADDRNOTAVAIL);
      +    } else if (sock->fd < 0
      +               && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      +                                     addr->ai_protocol))
      +                          < 0) {
      +        err = avs_errno(AVS_UNKNOWN_ERROR);
      +    } else if (connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      +        err = avs_errno(AVS_ECONNREFUSED);
      +    }
      +    if (avs_is_ok(err)) {
      +        sock->shut_down = false;
      +        snprintf(sock->remote_hostname, sizeof(sock->remote_hostname), "%s",
      +                 host);
      +    }
      +    freeaddrinfo(addr);
      +    return err;
      +}
       

      Note that in addition to saving the hostname, we also set the shut_down flag @@ -219,53 +216,53 @@

      8.3.1.4.3. < the “shut down” state.

      For this reason, we also need to update this flag in the bind and close operations:

      -
      static avs_error_t
      -net_bind(avs_net_socket_t *sock_, const char *address, const char *port) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct addrinfo hints = {
      -        .ai_flags = AI_PASSIVE,
      -        .ai_socktype = sock->socktype
      -    };
      -    if (sock->fd >= 0) {
      -        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      -                   &(socklen_t) { sizeof(hints.ai_family) });
      -    }
      -    struct addrinfo *addr = NULL;
      -    avs_error_t err = AVS_OK;
      -    if (getaddrinfo(address, port, &hints, &addr) || !addr) {
      -        err = avs_errno(AVS_EADDRNOTAVAIL);
      -    } else if ((sock->fd < 0
      -                && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      -                                      addr->ai_protocol))
      -                           < 0)
      -               || setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 },
      -                             sizeof(int))) {
      -        err = avs_errno(AVS_UNKNOWN_ERROR);
      -    } else if (bind(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      -        err = avs_errno(AVS_ECONNREFUSED);
      -    } else {
      -        sock->shut_down = false;
      -    }
      -    if (avs_is_err(err) && sock->fd >= 0) {
      -        close(sock->fd);
      -        sock->fd = -1;
      -    }
      -    freeaddrinfo(addr);
      -    return err;
      -}
      +
      static avs_error_t
      +net_bind(avs_net_socket_t *sock_, const char *address, const char *port) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct addrinfo hints = {
      +        .ai_flags = AI_PASSIVE,
      +        .ai_socktype = sock->socktype
      +    };
      +    if (sock->fd >= 0) {
      +        getsockopt(sock->fd, SOL_SOCKET, SO_DOMAIN, &hints.ai_family,
      +                   &(socklen_t) { sizeof(hints.ai_family) });
      +    }
      +    struct addrinfo *addr = NULL;
      +    avs_error_t err = AVS_OK;
      +    if (getaddrinfo(address, port, &hints, &addr) || !addr) {
      +        err = avs_errno(AVS_EADDRNOTAVAIL);
      +    } else if ((sock->fd < 0
      +                && (sock->fd = socket(addr->ai_family, addr->ai_socktype,
      +                                      addr->ai_protocol))
      +                           < 0)
      +               || setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 },
      +                             sizeof(int))) {
      +        err = avs_errno(AVS_UNKNOWN_ERROR);
      +    } else if (bind(sock->fd, addr->ai_addr, addr->ai_addrlen)) {
      +        err = avs_errno(AVS_ECONNREFUSED);
      +    } else {
      +        sock->shut_down = false;
      +    }
      +    if (avs_is_err(err) && sock->fd >= 0) {
      +        close(sock->fd);
      +        sock->fd = -1;
      +    }
      +    freeaddrinfo(addr);
      +    return err;
      +}
       
      -static avs_error_t net_close(avs_net_socket_t *sock_) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    avs_error_t err = AVS_OK;
      -    if (sock->fd >= 0) {
      -        if (close(sock->fd)) {
      -            err = avs_errno(AVS_EIO);
      -        }
      -        sock->fd = -1;
      -        sock->shut_down = false;
      -    }
      -    return err;
      -}
      +static avs_error_t net_close(avs_net_socket_t *sock_) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    avs_error_t err = AVS_OK;
      +    if (sock->fd >= 0) {
      +        if (close(sock->fd)) {
      +            err = avs_errno(AVS_EIO);
      +        }
      +        sock->fd = -1;
      +        sock->shut_down = false;
      +    }
      +    return err;
      +}
       

      @@ -273,90 +270,90 @@

      8.3.1.4.3. <

      8.3.1.4.4. Update to get_opt implementation

      We need to fix implementation of getting the AVS_NET_SOCKET_OPT_STATE option so that the “shut down” state is properly reported:

      -
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      -                               avs_net_socket_opt_key_t option_key,
      -                               avs_net_socket_opt_value_t *out_option_value) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    switch (option_key) {
      -    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      -        out_option_value->recv_timeout = sock->recv_timeout;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_STATE:
      -        if (sock->fd < 0) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      -        } else if (sock->shut_down) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_SHUTDOWN;
      -        } else {
      -            sockaddr_union_t addr;
      -            if (!getpeername(sock->fd, &addr.addr,
      -                             &(socklen_t) { sizeof(addr) })
      -                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      -                        || (addr.in6.sin6_family == AF_INET6
      -                            && addr.in6.sin6_port != 0))) {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      -            } else {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      -            }
      -        }
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_INNER_MTU:
      -        out_option_value->mtu = 1464;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      -        out_option_value->flag = false;
      -        return AVS_OK;
      -    default:
      -        return avs_errno(AVS_ENOTSUP);
      -    }
      -}
      +
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      +                               avs_net_socket_opt_key_t option_key,
      +                               avs_net_socket_opt_value_t *out_option_value) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    switch (option_key) {
      +    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      +        out_option_value->recv_timeout = sock->recv_timeout;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_STATE:
      +        if (sock->fd < 0) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      +        } else if (sock->shut_down) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_SHUTDOWN;
      +        } else {
      +            sockaddr_union_t addr;
      +            if (!getpeername(sock->fd, &addr.addr,
      +                             &(socklen_t) { sizeof(addr) })
      +                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      +                        || (addr.in6.sin6_family == AF_INET6
      +                            && addr.in6.sin6_port != 0))) {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      +            } else {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      +            }
      +        }
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_INNER_MTU:
      +        out_option_value->mtu = 1464;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      +        out_option_value->flag = false;
      +        return AVS_OK;
      +    default:
      +        return avs_errno(AVS_ENOTSUP);
      +    }
      +}
       

      8.3.1.4.5. New method implementations

      Implementation of the shutdown operation method is simple and self-explanatory:

      -
      static avs_error_t net_shutdown(avs_net_socket_t *sock_) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    avs_error_t err = avs_errno(AVS_EBADF);
      -    if (sock->fd >= 0) {
      -        err = shutdown(sock->fd, SHUT_RDWR) ? avs_errno(AVS_EIO) : AVS_OK;
      -        sock->shut_down = true;
      -    }
      -    return err;
      -}
      +
      static avs_error_t net_shutdown(avs_net_socket_t *sock_) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    avs_error_t err = avs_errno(AVS_EBADF);
      +    if (sock->fd >= 0) {
      +        err = shutdown(sock->fd, SHUT_RDWR) ? avs_errno(AVS_EIO) : AVS_OK;
      +        sock->shut_down = true;
      +    }
      +    return err;
      +}
       

      Similarly, the “get remote hostname” method code is just a simple string copy:

      -
      static avs_error_t net_remote_hostname(avs_net_socket_t *sock_,
      -                                       char *out_buffer,
      -                                       size_t out_buffer_size) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    return avs_simple_snprintf(out_buffer, out_buffer_size, "%s",
      -                               sock->remote_hostname)
      -                           < 0
      -                   ? avs_errno(AVS_UNKNOWN_ERROR)
      -                   : AVS_OK;
      -}
      +
      static avs_error_t net_remote_hostname(avs_net_socket_t *sock_,
      +                                       char *out_buffer,
      +                                       size_t out_buffer_size) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    return avs_simple_snprintf(out_buffer, out_buffer_size, "%s",
      +                               sock->remote_hostname)
      +                           < 0
      +                   ? avs_errno(AVS_UNKNOWN_ERROR)
      +                   : AVS_OK;
      +}
       

      Of course the newly implemented functions need to be referenced in the virtual method table:

      -
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      -    .connect = net_connect,
      -    .send = net_send,
      -    .receive = net_receive,
      -    .bind = net_bind,
      -    .close = net_close,
      -    .shutdown = net_shutdown,
      -    .cleanup = net_cleanup,
      -    .get_system_socket = net_system_socket,
      -    .get_remote_host = net_remote_host,
      -    .get_remote_hostname = net_remote_hostname,
      -    .get_remote_port = net_remote_port,
      -    .get_local_port = net_local_port,
      -    .get_opt = net_get_opt,
      -    .set_opt = net_set_opt
      -};
      +
      static const avs_net_socket_v_table_t NET_SOCKET_VTABLE = {
      +    .connect = net_connect,
      +    .send = net_send,
      +    .receive = net_receive,
      +    .bind = net_bind,
      +    .close = net_close,
      +    .shutdown = net_shutdown,
      +    .cleanup = net_cleanup,
      +    .get_system_socket = net_system_socket,
      +    .get_remote_host = net_remote_host,
      +    .get_remote_hostname = net_remote_hostname,
      +    .get_remote_port = net_remote_port,
      +    .get_local_port = net_local_port,
      +    .get_opt = net_get_opt,
      +    .set_opt = net_set_opt
      +};
       
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Stats.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Stats.html index 1c84e260..3fa52495 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Stats.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI-Stats.html @@ -4,13 +4,10 @@ - 8.3.1.5. Statistics support — Anjay 3.4.0 documentation + 8.3.1.5. Statistics support — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -84,10 +81,10 @@
      @@ -123,66 +120,66 @@

      8.3.1.5.1. <

      8.3.1.5.2. Additional socket state

      We need to store the number of bytes sent and received via the socket, so we add appropriate fields to the socket object:

      -
      typedef struct {
      -    const avs_net_socket_v_table_t *operations;
      -    int socktype;
      -    int fd;
      -    avs_time_duration_t recv_timeout;
      -    char remote_hostname[256];
      -    bool shut_down;
      -    size_t bytes_sent;
      -    size_t bytes_received;
      -} net_socket_impl_t;
      +
      typedef struct {
      +    const avs_net_socket_v_table_t *operations;
      +    int socktype;
      +    int fd;
      +    avs_time_duration_t recv_timeout;
      +    char remote_hostname[256];
      +    bool shut_down;
      +    size_t bytes_sent;
      +    size_t bytes_received;
      +} net_socket_impl_t;
       

      8.3.1.5.3. Updating the socket state

      We can now update these counters in the send and receive operations:

      -
      static avs_error_t
      -net_send(avs_net_socket_t *sock_, const void *buffer, size_t buffer_length) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    ssize_t written = send(sock->fd, buffer, buffer_length, MSG_NOSIGNAL);
      -    if (written >= 0) {
      -        sock->bytes_sent += (size_t) written;
      -        if ((size_t) written == buffer_length) {
      -            return AVS_OK;
      -        }
      -    }
      -    return avs_errno(AVS_EIO);
      -}
      +
      static avs_error_t
      +net_send(avs_net_socket_t *sock_, const void *buffer, size_t buffer_length) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    ssize_t written = send(sock->fd, buffer, buffer_length, MSG_NOSIGNAL);
      +    if (written >= 0) {
      +        sock->bytes_sent += (size_t) written;
      +        if ((size_t) written == buffer_length) {
      +            return AVS_OK;
      +        }
      +    }
      +    return avs_errno(AVS_EIO);
      +}
       
      -static avs_error_t net_receive(avs_net_socket_t *sock_,
      -                               size_t *out_bytes_received,
      -                               void *buffer,
      -                               size_t buffer_length) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    struct pollfd pfd = {
      -        .fd = sock->fd,
      -        .events = POLLIN
      -    };
      -    int64_t timeout_ms;
      -    if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
      -                                    sock->recv_timeout)) {
      -        timeout_ms = -1;
      -    } else if (timeout_ms < 0) {
      -        timeout_ms = 0;
      -    }
      -    if (poll(&pfd, 1, (int) timeout_ms) == 0) {
      -        return avs_errno(AVS_ETIMEDOUT);
      -    }
      -    ssize_t bytes_received = read(sock->fd, buffer, buffer_length);
      -    if (bytes_received < 0) {
      -        return avs_errno(AVS_EIO);
      -    }
      -    *out_bytes_received = (size_t) bytes_received;
      -    sock->bytes_received += (size_t) bytes_received;
      -    if (buffer_length > 0 && sock->socktype == SOCK_DGRAM
      -            && (size_t) bytes_received == buffer_length) {
      -        return avs_errno(AVS_EMSGSIZE);
      -    }
      -    return AVS_OK;
      -}
      +static avs_error_t net_receive(avs_net_socket_t *sock_,
      +                               size_t *out_bytes_received,
      +                               void *buffer,
      +                               size_t buffer_length) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    struct pollfd pfd = {
      +        .fd = sock->fd,
      +        .events = POLLIN
      +    };
      +    int64_t timeout_ms;
      +    if (avs_time_duration_to_scalar(&timeout_ms, AVS_TIME_MS,
      +                                    sock->recv_timeout)) {
      +        timeout_ms = -1;
      +    } else if (timeout_ms < 0) {
      +        timeout_ms = 0;
      +    }
      +    if (poll(&pfd, 1, (int) timeout_ms) == 0) {
      +        return avs_errno(AVS_ETIMEDOUT);
      +    }
      +    ssize_t bytes_received = read(sock->fd, buffer, buffer_length);
      +    if (bytes_received < 0) {
      +        return avs_errno(AVS_EIO);
      +    }
      +    *out_bytes_received = (size_t) bytes_received;
      +    sock->bytes_received += (size_t) bytes_received;
      +    if (buffer_length > 0 && sock->socktype == SOCK_DGRAM
      +            && (size_t) bytes_received == buffer_length) {
      +        return avs_errno(AVS_EMSGSIZE);
      +    }
      +    return AVS_OK;
      +}
       
      @@ -190,48 +187,48 @@

      8.3.1.5.3. <

      8.3.1.5.4. Update to get_opt implementation

      We need to add implementations of the AVS_NET_SOCKET_OPT_BYTES_SENT and AVS_NET_SOCKET_OPT_BYTES_RECEIVED options to net_get_opt():

      -
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      -                               avs_net_socket_opt_key_t option_key,
      -                               avs_net_socket_opt_value_t *out_option_value) {
      -    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      -    switch (option_key) {
      -    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      -        out_option_value->recv_timeout = sock->recv_timeout;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_STATE:
      -        if (sock->fd < 0) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      -        } else if (sock->shut_down) {
      -            out_option_value->state = AVS_NET_SOCKET_STATE_SHUTDOWN;
      -        } else {
      -            sockaddr_union_t addr;
      -            if (!getpeername(sock->fd, &addr.addr,
      -                             &(socklen_t) { sizeof(addr) })
      -                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      -                        || (addr.in6.sin6_family == AF_INET6
      -                            && addr.in6.sin6_port != 0))) {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      -            } else {
      -                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      -            }
      -        }
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_INNER_MTU:
      -        out_option_value->mtu = 1464;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      -        out_option_value->flag = false;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_BYTES_SENT:
      -        out_option_value->bytes_sent = sock->bytes_sent;
      -        return AVS_OK;
      -    case AVS_NET_SOCKET_OPT_BYTES_RECEIVED:
      -        out_option_value->bytes_received = sock->bytes_received;
      -        return AVS_OK;
      -    default:
      -        return avs_errno(AVS_ENOTSUP);
      -    }
      -}
      +
      static avs_error_t net_get_opt(avs_net_socket_t *sock_,
      +                               avs_net_socket_opt_key_t option_key,
      +                               avs_net_socket_opt_value_t *out_option_value) {
      +    net_socket_impl_t *sock = (net_socket_impl_t *) sock_;
      +    switch (option_key) {
      +    case AVS_NET_SOCKET_OPT_RECV_TIMEOUT:
      +        out_option_value->recv_timeout = sock->recv_timeout;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_STATE:
      +        if (sock->fd < 0) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_CLOSED;
      +        } else if (sock->shut_down) {
      +            out_option_value->state = AVS_NET_SOCKET_STATE_SHUTDOWN;
      +        } else {
      +            sockaddr_union_t addr;
      +            if (!getpeername(sock->fd, &addr.addr,
      +                             &(socklen_t) { sizeof(addr) })
      +                    && ((addr.in.sin_family == AF_INET && addr.in.sin_port != 0)
      +                        || (addr.in6.sin6_family == AF_INET6
      +                            && addr.in6.sin6_port != 0))) {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_CONNECTED;
      +            } else {
      +                out_option_value->state = AVS_NET_SOCKET_STATE_BOUND;
      +            }
      +        }
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_INNER_MTU:
      +        out_option_value->mtu = 1464;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_HAS_BUFFERED_DATA:
      +        out_option_value->flag = false;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_BYTES_SENT:
      +        out_option_value->bytes_sent = sock->bytes_sent;
      +        return AVS_OK;
      +    case AVS_NET_SOCKET_OPT_BYTES_RECEIVED:
      +        out_option_value->bytes_received = sock->bytes_received;
      +        return AVS_OK;
      +    default:
      +        return avs_errno(AVS_ENOTSUP);
      +    }
      +}
       

      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI1.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI1.html index a225ff7d..ef093067 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI1.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI1.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI2.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI2.html index 3755a240..02120f7c 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI2.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI2.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI3.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI3.html index 729c2c81..e6a5cef8 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI3.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI3.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI4.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI4.html index 27f56eb5..ae10f25e 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI4.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI4.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI5.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI5.html index 67cab102..09e973ed 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI5.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI5.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI6.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI6.html index f90e8c09..9210035d 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI6.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI6.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI7.html b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI7.html index 2be02939..7de49bcb 100644 --- a/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI7.html +++ b/PortingGuideForNonPOSIXPlatforms/NetworkingAPI/NetworkingAPI7.html @@ -5,13 +5,10 @@ - Redirection — Anjay 3.4.0 documentation + Redirection — Anjay 3.4.1 documentation - @@ -31,7 +28,7 @@
      - 3.4.0 + 3.4.1
      @@ -67,8 +64,8 @@
        -
      • - +
      • »
      • +
      • Redirection
      diff --git a/PortingGuideForNonPOSIXPlatforms/ThreadingAPI.html b/PortingGuideForNonPOSIXPlatforms/ThreadingAPI.html index 19c883f7..78870c03 100644 --- a/PortingGuideForNonPOSIXPlatforms/ThreadingAPI.html +++ b/PortingGuideForNonPOSIXPlatforms/ThreadingAPI.html @@ -4,13 +4,10 @@ - 8.2. Threading API — Anjay 3.4.0 documentation + 8.2. Threading API — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -74,9 +71,9 @@
      diff --git a/PortingGuideForNonPOSIXPlatforms/TimeAPI.html b/PortingGuideForNonPOSIXPlatforms/TimeAPI.html index cdcf9fe2..2b42b0a2 100644 --- a/PortingGuideForNonPOSIXPlatforms/TimeAPI.html +++ b/PortingGuideForNonPOSIXPlatforms/TimeAPI.html @@ -4,13 +4,10 @@ - 8.1. Time API — Anjay 3.4.0 documentation + 8.1. Time API — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -74,9 +71,9 @@
      @@ -118,14 +115,14 @@

      8.1.2. Reference implementation

      In the reference POSIX-based implementation, it is a simple wrapper for the CLOCK_REALITME clock.

      -
      avs_time_real_t avs_time_real_now(void) {
      -    struct timespec system_value;
      -    avs_time_real_t result;
      -    clock_gettime(CLOCK_REALTIME, &system_value);
      -    result.since_real_epoch.seconds = system_value.tv_sec;
      -    result.since_real_epoch.nanoseconds = (int32_t) system_value.tv_nsec;
      -    return result;
      -}
      +
      avs_time_real_t avs_time_real_now(void) {
      +    struct timespec system_value;
      +    avs_time_real_t result;
      +    clock_gettime(CLOCK_REALTIME, &system_value);
      +    result.since_real_epoch.seconds = system_value.tv_sec;
      +    result.since_real_epoch.nanoseconds = (int32_t) system_value.tv_nsec;
      +    return result;
      +}
       
      @@ -142,21 +139,21 @@

      8.1.2.2. avs_time_monotonic_now()This is used in the reference implementation - it is generally a wrapper for the CLOCK_MONOTONIC clock, but on some platforms it is not available - CLOCK_REALTIME is used in these cases.

      -
      avs_time_monotonic_t avs_time_monotonic_now(void) {
      -    struct timespec system_value;
      -    avs_time_monotonic_t result;
      +
      avs_time_monotonic_t avs_time_monotonic_now(void) {
      +    struct timespec system_value;
      +    avs_time_monotonic_t result;
       #    ifdef CLOCK_MONOTONIC
      -    if (clock_gettime(CLOCK_MONOTONIC, &system_value))
      +    if (clock_gettime(CLOCK_MONOTONIC, &system_value))
       #    endif
      -    {
      +    {
               // CLOCK_MONOTONIC is not mandatory in POSIX;
               // fallback to REALTIME if we don't have it
      -        clock_gettime(CLOCK_REALTIME, &system_value);
      -    }
      -    result.since_monotonic_epoch.seconds = system_value.tv_sec;
      -    result.since_monotonic_epoch.nanoseconds = (int32_t) system_value.tv_nsec;
      -    return result;
      -}
      +        clock_gettime(CLOCK_REALTIME, &system_value);
      +    }
      +    result.since_monotonic_epoch.seconds = system_value.tv_sec;
      +    result.since_monotonic_epoch.nanoseconds = (int32_t) system_value.tv_nsec;
      +    return result;
      +}
       
      diff --git a/Tools.html b/Tools.html index 6793d995..b8415dc6 100644 --- a/Tools.html +++ b/Tools.html @@ -4,13 +4,10 @@ - 7. Tools — Anjay 3.4.0 documentation + 7. Tools — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -73,8 +70,8 @@
        -
      • - +
      • »
      • +
      • 7. Tools
      diff --git a/Tools/CliLwM2MServer.html b/Tools/CliLwM2MServer.html index cdf1da3c..f053a83d 100644 --- a/Tools/CliLwM2MServer.html +++ b/Tools/CliLwM2MServer.html @@ -4,13 +4,10 @@ - 7.1. LwM2M testing shell — Anjay 3.4.0 documentation + 7.1. LwM2M testing shell — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -73,9 +70,9 @@
        -
      • - - +
      • »
      • +
      • 7. Tools »
      • +
      • 7.1. LwM2M testing shell
      @@ -123,11 +120,11 @@

      7.1.2. Supported commands
      ./bootstrap/framework/nsh-lwm2m/nsh_lwm2m.py -l 9000
      +
      ./bootstrap/framework/nsh-lwm2m/nsh_lwm2m.py -l 9000
       

      and the second for setting up the client:

      -
      ./output/bin/demo -e my_endpoint -u coap://127.0.0.1:9000
      +
      ./output/bin/demo -e my_endpoint -u coap://127.0.0.1:9000
       
      diff --git a/Tools/FactoryProvisioning.html b/Tools/FactoryProvisioning.html index 308061d5..84742c50 100644 --- a/Tools/FactoryProvisioning.html +++ b/Tools/FactoryProvisioning.html @@ -4,13 +4,10 @@ - 7.2. Factory Provisioning Tool — Anjay 3.4.0 documentation + 7.2. Factory Provisioning Tool — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -73,9 +70,9 @@
        -
      • - - +
      • »
      • +
      • 7. Tools »
      • +
      • 7.2. Factory Provisioning Tool
      diff --git a/Tools/StubGenerator.html b/Tools/StubGenerator.html index eb489584..567aaf0b 100644 --- a/Tools/StubGenerator.html +++ b/Tools/StubGenerator.html @@ -4,13 +4,10 @@ - 7.3. Anjay Object stub generator — Anjay 3.4.0 documentation + 7.3. Anjay Object stub generator — Anjay 3.4.1 documentation - @@ -32,7 +29,7 @@
      - 3.4.0 + 3.4.1
      @@ -73,9 +70,9 @@
        -
      • - - +
      • »
      • +
      • 7. Tools »
      • +
      • 7.3. Anjay Object stub generator
      @@ -124,48 +121,48 @@

      7.3.2.1.
      <?xml version="1.0" encoding="utf-8"?>
      -<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd">
      -  <Object ObjectType="MODefinition">
      -        <Name>Some Object Name</Name>
      -        <Description1><![CDATA[LwM2M Object description.]]></Description1>
      -        <ObjectID>9999</ObjectID>
      -        <ObjectURN></ObjectURN>
      -        <MultipleInstances>Multiple</MultipleInstances>
      -        <Mandatory>Optional</Mandatory>
      -        <Resources>
      -            <Item ID="0">
      -                <Name>Some String Resource</Name>
      -                <Operations>RW</Operations>
      -                <MultipleInstances>Single</MultipleInstances>
      -                <Mandatory>Mandatory</Mandatory>
      -                <Type>String</Type>
      -                <RangeEnumeration></RangeEnumeration>
      -                <Units></Units>
      -                <Description><![CDATA[Some description.]]></Description>
      -            </Item>
      -            <Item ID="1">
      -                <Name>Some Integer Resource</Name>
      -                <Operations>RW</Operations>
      -                <MultipleInstances>Single</MultipleInstances>
      -                <Mandatory>Mandatory</Mandatory>
      -                <Type>Integer</Type>
      -                <RangeEnumeration></RangeEnumeration>
      -                <Units></Units>
      -                <Description><![CDATA[Some description.]]></Description>
      -            </Item>
      -            <Item ID="2">
      -                <Name>Some Boolean Multiple Resource</Name>
      -                <Operations>RW</Operations>
      -                <MultipleInstances>Multiple</MultipleInstances>
      -                <Mandatory>Mandatory</Mandatory>
      -                <Type>Boolean</Type>
      -                <RangeEnumeration/>
      -                <Units></Units>
      -                <Description><![CDATA[Some description.]]></Description>
      -            </Item>
      -        </Resources>
      -        <Description2></Description2>
      -    </Object>
      +<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd">
      +  <Object ObjectType="MODefinition">
      +        <Name>Some Object Name</Name>
      +        <Description1><![CDATA[LwM2M Object description.]]></Description1>
      +        <ObjectID>9999</ObjectID>
      +        <ObjectURN></ObjectURN>
      +        <MultipleInstances>Multiple</MultipleInstances>
      +        <Mandatory>Optional</Mandatory>
      +        <Resources>
      +            <Item ID="0">
      +                <Name>Some String Resource</Name>
      +                <Operations>RW</Operations>
      +                <MultipleInstances>Single</MultipleInstances>
      +                <Mandatory>Mandatory</Mandatory>
      +                <Type>String</Type>
      +                <RangeEnumeration></RangeEnumeration>
      +                <Units></Units>
      +                <Description><![CDATA[Some description.]]></Description>
      +            </Item>
      +            <Item ID="1">
      +                <Name>Some Integer Resource</Name>
      +                <Operations>RW</Operations>
      +                <MultipleInstances>Single</MultipleInstances>
      +                <Mandatory>Mandatory</Mandatory>
      +                <Type>Integer</Type>
      +                <RangeEnumeration></RangeEnumeration>
      +                <Units></Units>
      +                <Description><![CDATA[Some description.]]></Description>
      +            </Item>
      +            <Item ID="2">
      +                <Name>Some Boolean Multiple Resource</Name>
      +                <Operations>RW</Operations>
      +                <MultipleInstances>Multiple</MultipleInstances>
      +                <Mandatory>Mandatory</Mandatory>
      +                <Type>Boolean</Type>
      +                <RangeEnumeration/>
      +                <Units></Units>
      +                <Description><![CDATA[Some description.]]></Description>
      +            </Item>
      +        </Resources>
      +        <Description2></Description2>
      +    </Object>
       </LWM2M>
       
      @@ -180,7 +177,7 @@

      7.3.2.1. 7.3.2.2. Object with dynamically-allocated instances

      To create a C language object template with dynamically-allocated instances we execute the following command:

      -
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.c
      +
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.c
       

      The source of the example object looks like this:

      @@ -193,343 +190,343 @@

      7.3.2.2. * ID: 9999, URN: , Optional, Multiple * * LwM2M Object description. - */ -#include <assert.h> -#include <stdbool.h> + */ +#include <assert.h> +#include <stdbool.h> -#include <anjay/anjay.h> -#include <avsystem/commons/avs_defs.h> -#include <avsystem/commons/avs_list.h> -#include <avsystem/commons/avs_memory.h> +#include <anjay/anjay.h> +#include <avsystem/commons/avs_defs.h> +#include <avsystem/commons/avs_list.h> +#include <avsystem/commons/avs_memory.h> /** * Some String Resource: RW, Single, Mandatory * type: string, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_STRING_RESOURCE 0 /** * Some Integer Resource: RW, Single, Mandatory * type: integer, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_INTEGER_RESOURCE 1 /** * Some Boolean Multiple Resource: RW, Multiple, Mandatory * type: boolean, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_BOOLEAN_MULTIPLE_RESOURCE 2 -typedef struct some_object_name_instance_struct { - anjay_iid_t iid; +typedef struct some_object_name_instance_struct { + anjay_iid_t iid; // TODO: instance state -} some_object_name_instance_t; +} some_object_name_instance_t; -typedef struct some_object_name_object_struct { - const anjay_dm_object_def_t *def; - AVS_LIST(some_object_name_instance_t) instances; +typedef struct some_object_name_object_struct { + const anjay_dm_object_def_t *def; + AVS_LIST(some_object_name_instance_t) instances; // TODO: object state -} some_object_name_object_t; - -static inline some_object_name_object_t * -get_obj(const anjay_dm_object_def_t *const *obj_ptr) { - assert(obj_ptr); - return AVS_CONTAINER_OF(obj_ptr, some_object_name_object_t, def); -} - -static some_object_name_instance_t *find_instance(const some_object_name_object_t *obj, - anjay_iid_t iid) { - AVS_LIST(some_object_name_instance_t) it; - AVS_LIST_FOREACH(it, obj->instances) { - if (it->iid == iid) { - return it; - } else if (it->iid > iid) { - break; - } - } - - return NULL; -} - -static int list_instances(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_dm_list_ctx_t *ctx) { - (void) anjay; - - AVS_LIST(some_object_name_instance_t) it; - AVS_LIST_FOREACH(it, get_obj(obj_ptr)->instances) { - anjay_dm_emit(ctx, it->iid); - } - - return 0; -} - -static int init_instance(some_object_name_instance_t *inst, anjay_iid_t iid) { - assert(iid != ANJAY_ID_INVALID); - - inst->iid = iid; +} some_object_name_object_t; + +static inline some_object_name_object_t * +get_obj(const anjay_dm_object_def_t *const *obj_ptr) { + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, some_object_name_object_t, def); +} + +static some_object_name_instance_t *find_instance(const some_object_name_object_t *obj, + anjay_iid_t iid) { + AVS_LIST(some_object_name_instance_t) it; + AVS_LIST_FOREACH(it, obj->instances) { + if (it->iid == iid) { + return it; + } else if (it->iid > iid) { + break; + } + } + + return NULL; +} + +static int list_instances(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_dm_list_ctx_t *ctx) { + (void) anjay; + + AVS_LIST(some_object_name_instance_t) it; + AVS_LIST_FOREACH(it, get_obj(obj_ptr)->instances) { + anjay_dm_emit(ctx, it->iid); + } + + return 0; +} + +static int init_instance(some_object_name_instance_t *inst, anjay_iid_t iid) { + assert(iid != ANJAY_ID_INVALID); + + inst->iid = iid; // TODO: instance init // TODO: return 0 on success, negative value on failure - return 0; -} + return 0; +} -static void release_instance(some_object_name_instance_t *inst) { +static void release_instance(some_object_name_instance_t *inst) { // TODO: instance cleanup - (void) inst; -} - -static some_object_name_instance_t * -add_instance(some_object_name_object_t *obj, anjay_iid_t iid) { - assert(find_instance(obj, iid) == NULL); - - AVS_LIST(some_object_name_instance_t) created = - AVS_LIST_NEW_ELEMENT(some_object_name_instance_t); - if (!created) { - return NULL; - } - - int result = init_instance(created, iid); - if (result) { - AVS_LIST_CLEAR(&created); - return NULL; - } - - AVS_LIST(some_object_name_instance_t) *ptr; - AVS_LIST_FOREACH_PTR(ptr, &obj->instances) { - if ((*ptr)->iid > created->iid) { - break; - } - } - - AVS_LIST_INSERT(ptr, created); - return created; -} - -static int instance_create(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid) { - (void) anjay; - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - - return add_instance(obj, iid) ? 0 : ANJAY_ERR_INTERNAL; -} - -static int instance_remove(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid) { - (void) anjay; - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - - AVS_LIST(some_object_name_instance_t) *it; - AVS_LIST_FOREACH_PTR(it, &obj->instances) { - if ((*it)->iid == iid) { - release_instance(*it); - AVS_LIST_DELETE(it); - return 0; - } else if ((*it)->iid > iid) { - break; - } - } - - assert(0); - return ANJAY_ERR_NOT_FOUND; -} - -static int instance_reset(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - some_object_name_instance_t *inst = find_instance(obj, iid); - assert(inst); + (void) inst; +} + +static some_object_name_instance_t * +add_instance(some_object_name_object_t *obj, anjay_iid_t iid) { + assert(find_instance(obj, iid) == NULL); + + AVS_LIST(some_object_name_instance_t) created = + AVS_LIST_NEW_ELEMENT(some_object_name_instance_t); + if (!created) { + return NULL; + } + + int result = init_instance(created, iid); + if (result) { + AVS_LIST_CLEAR(&created); + return NULL; + } + + AVS_LIST(some_object_name_instance_t) *ptr; + AVS_LIST_FOREACH_PTR(ptr, &obj->instances) { + if ((*ptr)->iid > created->iid) { + break; + } + } + + AVS_LIST_INSERT(ptr, created); + return created; +} + +static int instance_create(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + + return add_instance(obj, iid) ? 0 : ANJAY_ERR_INTERNAL; +} + +static int instance_remove(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + + AVS_LIST(some_object_name_instance_t) *it; + AVS_LIST_FOREACH_PTR(it, &obj->instances) { + if ((*it)->iid == iid) { + release_instance(*it); + AVS_LIST_DELETE(it); + return 0; + } else if ((*it)->iid > iid) { + break; + } + } + + assert(0); + return ANJAY_ERR_NOT_FOUND; +} + +static int instance_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + some_object_name_instance_t *inst = find_instance(obj, iid); + assert(inst); // TODO: instance reset - return 0; -} - -static int list_resources(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_dm_resource_list_ctx_t *ctx) { - (void) anjay; - (void) obj_ptr; - (void) iid; - - anjay_dm_emit_res(ctx, RID_SOME_STRING_RESOURCE, - ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_SOME_INTEGER_RESOURCE, - ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_SOME_BOOLEAN_MULTIPLE_RESOURCE, - ANJAY_DM_RES_RWM, ANJAY_DM_RES_PRESENT); - return 0; -} - -static int resource_read(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_riid_t riid, - anjay_output_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - some_object_name_instance_t *inst = find_instance(obj, iid); - assert(inst); - - switch (rid) { - case RID_SOME_STRING_RESOURCE: - assert(riid == ANJAY_ID_INVALID); + return 0; +} + +static int list_resources(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_dm_resource_list_ctx_t *ctx) { + (void) anjay; + (void) obj_ptr; + (void) iid; + + anjay_dm_emit_res(ctx, RID_SOME_STRING_RESOURCE, + ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_SOME_INTEGER_RESOURCE, + ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_SOME_BOOLEAN_MULTIPLE_RESOURCE, + ANJAY_DM_RES_RWM, ANJAY_DM_RES_PRESENT); + return 0; +} + +static int resource_read(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_output_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + some_object_name_instance_t *inst = find_instance(obj, iid); + assert(inst); + + switch (rid) { + case RID_SOME_STRING_RESOURCE: + assert(riid == ANJAY_ID_INVALID); return anjay_ret_string(ctx, ""); // TODO - case RID_SOME_INTEGER_RESOURCE: - assert(riid == ANJAY_ID_INVALID); + case RID_SOME_INTEGER_RESOURCE: + assert(riid == ANJAY_ID_INVALID); return anjay_ret_i32(ctx, 0); // TODO - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: // TODO: extract Resource Instance return anjay_ret_bool(ctx, 0); // TODO - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int resource_write(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_riid_t riid, - anjay_input_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - some_object_name_instance_t *inst = find_instance(obj, iid); - assert(inst); - - switch (rid) { - case RID_SOME_STRING_RESOURCE: { - assert(riid == ANJAY_ID_INVALID); + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_write(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_input_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + some_object_name_instance_t *inst = find_instance(obj, iid); + assert(inst); + + switch (rid) { + case RID_SOME_STRING_RESOURCE: { + assert(riid == ANJAY_ID_INVALID); char value[256]; // TODO return anjay_get_string(ctx, value, sizeof(value)); // TODO - } + } - case RID_SOME_INTEGER_RESOURCE: { - assert(riid == ANJAY_ID_INVALID); + case RID_SOME_INTEGER_RESOURCE: { + assert(riid == ANJAY_ID_INVALID); int32_t value; // TODO return anjay_get_i32(ctx, &value); // TODO - } + } - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: { // TODO: extract Resource Instance bool value; // TODO return anjay_get_bool(ctx, &value); // TODO - } - - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int resource_reset(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - some_object_name_instance_t *inst = find_instance(obj, iid); - assert(inst); - - switch (rid) { - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + } + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + some_object_name_instance_t *inst = find_instance(obj, iid); + assert(inst); + + switch (rid) { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: return ANJAY_ERR_NOT_IMPLEMENTED; // TODO: remove all Resource Instances - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int list_resource_instances(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_dm_list_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - some_object_name_instance_t *inst = find_instance(obj, iid); - assert(inst); - - switch (rid) { - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int list_resource_instances(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_dm_list_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + some_object_name_instance_t *inst = find_instance(obj, iid); + assert(inst); + + switch (rid) { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: // anjay_dm_emit(ctx, ...); // TODO - return 0; - - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static const anjay_dm_object_def_t OBJ_DEF = { - .oid = 9999, - .handlers = { - .list_instances = list_instances, - .instance_create = instance_create, - .instance_remove = instance_remove, - .instance_reset = instance_reset, - - .list_resources = list_resources, - .resource_read = resource_read, - .resource_write = resource_write, - .resource_reset = resource_reset, - .list_resource_instances = list_resource_instances, + return 0; + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static const anjay_dm_object_def_t OBJ_DEF = { + .oid = 9999, + .handlers = { + .list_instances = list_instances, + .instance_create = instance_create, + .instance_remove = instance_remove, + .instance_reset = instance_reset, + + .list_resources = list_resources, + .resource_read = resource_read, + .resource_write = resource_write, + .resource_reset = resource_reset, + .list_resource_instances = list_resource_instances, // TODO: implement these if transactional write/create is required - .transaction_begin = anjay_dm_transaction_NOOP, - .transaction_validate = anjay_dm_transaction_NOOP, - .transaction_commit = anjay_dm_transaction_NOOP, - .transaction_rollback = anjay_dm_transaction_NOOP, - } -}; - -const anjay_dm_object_def_t **some_object_name_object_create(void) { - some_object_name_object_t *obj = (some_object_name_object_t *) avs_calloc(1, sizeof(some_object_name_object_t)); - if (!obj) { - return NULL; - } - obj->def = &OBJ_DEF; + .transaction_begin = anjay_dm_transaction_NOOP, + .transaction_validate = anjay_dm_transaction_NOOP, + .transaction_commit = anjay_dm_transaction_NOOP, + .transaction_rollback = anjay_dm_transaction_NOOP, + } +}; + +const anjay_dm_object_def_t **some_object_name_object_create(void) { + some_object_name_object_t *obj = (some_object_name_object_t *) avs_calloc(1, sizeof(some_object_name_object_t)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; // TODO: object init - return &obj->def; -} + return &obj->def; +} -void some_object_name_object_release(const anjay_dm_object_def_t **def) { - if (def) { - some_object_name_object_t *obj = get_obj(def); - AVS_LIST_CLEAR(&obj->instances) { - release_instance(obj->instances); - } +void some_object_name_object_release(const anjay_dm_object_def_t **def) { + if (def) { + some_object_name_object_t *obj = get_obj(def); + AVS_LIST_CLEAR(&obj->instances) { + release_instance(obj->instances); + } // TODO: object cleanup - avs_free(obj); - } -} + avs_free(obj); + } +}

      @@ -558,7 +555,7 @@

      7.3.2.2. 7.3.2.3. Object with statically-allocated instances

      To create a C language object template with fixed 10 instances we use the -n switch:

      -
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.c -n 10
      +
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.c -n 10
       

      The resulting code is following:

      @@ -571,250 +568,250 @@

      7.3.2.3. * ID: 9999, URN: , Optional, Multiple * * LwM2M Object description. - */ -#include <assert.h> -#include <stdbool.h> + */ +#include <assert.h> +#include <stdbool.h> -#include <anjay/anjay.h> -#include <avsystem/commons/avs_defs.h> -#include <avsystem/commons/avs_memory.h> +#include <anjay/anjay.h> +#include <avsystem/commons/avs_defs.h> +#include <avsystem/commons/avs_memory.h> /** * Some String Resource: RW, Single, Mandatory * type: string, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_STRING_RESOURCE 0 /** * Some Integer Resource: RW, Single, Mandatory * type: integer, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_INTEGER_RESOURCE 1 /** * Some Boolean Multiple Resource: RW, Multiple, Mandatory * type: boolean, range: N/A, unit: N/A * Some description. - */ + */ #define RID_SOME_BOOLEAN_MULTIPLE_RESOURCE 2 -typedef struct some_object_name_instance_struct { +typedef struct some_object_name_instance_struct { // TODO: instance state -} some_object_name_instance_t; +} some_object_name_instance_t; -typedef struct some_object_name_object_struct { - const anjay_dm_object_def_t *def; - some_object_name_instance_t instances[10]; +typedef struct some_object_name_object_struct { + const anjay_dm_object_def_t *def; + some_object_name_instance_t instances[10]; // TODO: object state -} some_object_name_object_t; +} some_object_name_object_t; -static inline some_object_name_object_t * -get_obj(const anjay_dm_object_def_t *const *obj_ptr) { - assert(obj_ptr); - return AVS_CONTAINER_OF(obj_ptr, some_object_name_object_t, def); -} +static inline some_object_name_object_t * +get_obj(const anjay_dm_object_def_t *const *obj_ptr) { + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, some_object_name_object_t, def); +} -static int list_instances(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_dm_list_ctx_t *ctx) { - (void) anjay; +static int list_instances(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_dm_list_ctx_t *ctx) { + (void) anjay; - some_object_name_object_t *obj = get_obj(obj_ptr); - for (anjay_iid_t iid = 0; iid < AVS_ARRAY_SIZE(obj->instances); iid++) { - anjay_dm_emit(ctx, iid); - } + some_object_name_object_t *obj = get_obj(obj_ptr); + for (anjay_iid_t iid = 0; iid < AVS_ARRAY_SIZE(obj->instances); iid++) { + anjay_dm_emit(ctx, iid); + } - return 0; -} + return 0; +} -static int instance_reset(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid) { - (void) anjay; +static int instance_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - assert(iid < AVS_ARRAY_SIZE(obj->instances)); - some_object_name_instance_t *inst = &obj->instances[iid]; + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid < AVS_ARRAY_SIZE(obj->instances)); + some_object_name_instance_t *inst = &obj->instances[iid]; // TODO: instance reset // TODO: return 0 on success, negative value on failure - return 0; -} - -static int list_resources(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_dm_resource_list_ctx_t *ctx) { - (void) anjay; - (void) obj_ptr; - (void) iid; - - anjay_dm_emit_res(ctx, RID_SOME_STRING_RESOURCE, - ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_SOME_INTEGER_RESOURCE, - ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_SOME_BOOLEAN_MULTIPLE_RESOURCE, - ANJAY_DM_RES_RWM, ANJAY_DM_RES_PRESENT); - return 0; -} - -static int resource_read(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_riid_t riid, - anjay_output_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - assert(iid < AVS_ARRAY_SIZE(obj->instances)); - some_object_name_instance_t *inst = &obj->instances[iid]; - - switch (rid) { - case RID_SOME_STRING_RESOURCE: - assert(riid == ANJAY_ID_INVALID); + return 0; +} + +static int list_resources(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_dm_resource_list_ctx_t *ctx) { + (void) anjay; + (void) obj_ptr; + (void) iid; + + anjay_dm_emit_res(ctx, RID_SOME_STRING_RESOURCE, + ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_SOME_INTEGER_RESOURCE, + ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_SOME_BOOLEAN_MULTIPLE_RESOURCE, + ANJAY_DM_RES_RWM, ANJAY_DM_RES_PRESENT); + return 0; +} + +static int resource_read(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_output_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid < AVS_ARRAY_SIZE(obj->instances)); + some_object_name_instance_t *inst = &obj->instances[iid]; + + switch (rid) { + case RID_SOME_STRING_RESOURCE: + assert(riid == ANJAY_ID_INVALID); return anjay_ret_string(ctx, ""); // TODO - case RID_SOME_INTEGER_RESOURCE: - assert(riid == ANJAY_ID_INVALID); + case RID_SOME_INTEGER_RESOURCE: + assert(riid == ANJAY_ID_INVALID); return anjay_ret_i32(ctx, 0); // TODO - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: // TODO: extract Resource Instance return anjay_ret_bool(ctx, 0); // TODO - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int resource_write(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_riid_t riid, - anjay_input_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - assert(iid < AVS_ARRAY_SIZE(obj->instances)); - some_object_name_instance_t *inst = &obj->instances[iid]; - - switch (rid) { - case RID_SOME_STRING_RESOURCE: { - assert(riid == ANJAY_ID_INVALID); + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_write(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_input_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid < AVS_ARRAY_SIZE(obj->instances)); + some_object_name_instance_t *inst = &obj->instances[iid]; + + switch (rid) { + case RID_SOME_STRING_RESOURCE: { + assert(riid == ANJAY_ID_INVALID); char value[256]; // TODO return anjay_get_string(ctx, value, sizeof(value)); // TODO - } + } - case RID_SOME_INTEGER_RESOURCE: { - assert(riid == ANJAY_ID_INVALID); + case RID_SOME_INTEGER_RESOURCE: { + assert(riid == ANJAY_ID_INVALID); int32_t value; // TODO return anjay_get_i32(ctx, &value); // TODO - } + } - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: { // TODO: extract Resource Instance bool value; // TODO return anjay_get_bool(ctx, &value); // TODO - } - - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int resource_reset(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - assert(iid < AVS_ARRAY_SIZE(obj->instances)); - some_object_name_instance_t *inst = &obj->instances[iid]; - - switch (rid) { - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + } + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid < AVS_ARRAY_SIZE(obj->instances)); + some_object_name_instance_t *inst = &obj->instances[iid]; + + switch (rid) { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: return ANJAY_ERR_NOT_IMPLEMENTED; // TODO: remove all Resource Instances - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int list_resource_instances(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_dm_list_ctx_t *ctx) { - (void) anjay; - - some_object_name_object_t *obj = get_obj(obj_ptr); - assert(obj); - assert(iid < AVS_ARRAY_SIZE(obj->instances)); - some_object_name_instance_t *inst = &obj->instances[iid]; - - switch (rid) { - case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int list_resource_instances(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_dm_list_ctx_t *ctx) { + (void) anjay; + + some_object_name_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid < AVS_ARRAY_SIZE(obj->instances)); + some_object_name_instance_t *inst = &obj->instances[iid]; + + switch (rid) { + case RID_SOME_BOOLEAN_MULTIPLE_RESOURCE: // anjay_dm_emit(ctx, ...); // TODO - return 0; + return 0; - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} -static const anjay_dm_object_def_t OBJ_DEF = { - .oid = 9999, - .handlers = { - .list_instances = list_instances, - .instance_reset = instance_reset, +static const anjay_dm_object_def_t OBJ_DEF = { + .oid = 9999, + .handlers = { + .list_instances = list_instances, + .instance_reset = instance_reset, - .list_resources = list_resources, - .resource_read = resource_read, - .resource_write = resource_write, - .resource_reset = resource_reset, - .list_resource_instances = list_resource_instances, + .list_resources = list_resources, + .resource_read = resource_read, + .resource_write = resource_write, + .resource_reset = resource_reset, + .list_resource_instances = list_resource_instances, // TODO: implement these if transactional write/create is required - .transaction_begin = anjay_dm_transaction_NOOP, - .transaction_validate = anjay_dm_transaction_NOOP, - .transaction_commit = anjay_dm_transaction_NOOP, - .transaction_rollback = anjay_dm_transaction_NOOP - } -}; - -const anjay_dm_object_def_t **some_object_name_object_create(void) { - some_object_name_object_t *obj = - (some_object_name_object_t *) avs_calloc(1, sizeof(some_object_name_object_t)); - if (!obj) { - return NULL; - } - obj->def = &OBJ_DEF; + .transaction_begin = anjay_dm_transaction_NOOP, + .transaction_validate = anjay_dm_transaction_NOOP, + .transaction_commit = anjay_dm_transaction_NOOP, + .transaction_rollback = anjay_dm_transaction_NOOP + } +}; + +const anjay_dm_object_def_t **some_object_name_object_create(void) { + some_object_name_object_t *obj = + (some_object_name_object_t *) avs_calloc(1, sizeof(some_object_name_object_t)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; // TODO: object init - return &obj->def; -} + return &obj->def; +} -void some_object_name_object_release(const anjay_dm_object_def_t **def) { - if (def) { - some_object_name_object_t *obj = get_obj(def); +void some_object_name_object_release(const anjay_dm_object_def_t **def) { + if (def) { + some_object_name_object_t *obj = get_obj(def); // TODO: object cleanup - avs_free(obj); - } -} + avs_free(obj); + } +}

      @@ -832,11 +829,11 @@

      7.3.2.4. The script is capable of generating C++ object templates as well - the -x switch is intended to be used in this case. So, in order to create a C++ object with dynamic instances one has to execute the command:

      -
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.cpp -x
      +
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.cpp -x
       

      To create a C++ template of the same object with 10 static instances run:

      -
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.cpp -n 10 -x
      +
      ./tools/anjay_codegen.py -i some_object.xml -o some_object.cpp -n 10 -x
       

      The main difference between the two is that the former approach uses the C++ @@ -855,21 +852,21 @@

      7.3.3.

      7.3.4. Additional examples

      # list registered LwM2M objects
      -./tools/lwm2m_object_registry.py --list
      +./tools/lwm2m_object_registry.py --list
       
       # download Object Definition XML for object 3 (Device) to device.xml
      -./tools/lwm2m_object_registry.py --get-xml 3 > device.xml
      +./tools/lwm2m_object_registry.py --get-xml 3 > device.xml
       
       # generate object code stub from device.xml
      -./tools/anjay_codegen.py -i device.xml -o device.c
      +./tools/anjay_codegen.py -i device.xml -o device.c
       
       # download Object Definition XML for object 3 and generate code stub
       # without creating an intermediate file
      -./tools/lwm2m_object_registry.py --get-xml 3 | ./tools/anjay_codegen.py -i - -o device.c
      +./tools/lwm2m_object_registry.py --get-xml 3 | ./tools/anjay_codegen.py -i - -o device.c
       
       # download Object Definition XML for object 3303 and generate code stub with
       # five statically allocated instances without creating an intermediate file
      -./tools/lwm2m_object_registry.py --get-xml 3303 | ./tools/anjay_codegen.py -i - -o temperature.c -n 5
      +./tools/lwm2m_object_registry.py --get-xml 3303 | ./tools/anjay_codegen.py -i - -o temperature.c -n 5
       

      diff --git a/_sources/CommercialFeatures/CF-SmartCardBootstrap.rst.txt b/_sources/CommercialFeatures/CF-SmartCardBootstrap.rst.txt index 49279f9c..4479b47a 100644 --- a/_sources/CommercialFeatures/CF-SmartCardBootstrap.rst.txt +++ b/_sources/CommercialFeatures/CF-SmartCardBootstrap.rst.txt @@ -6,8 +6,8 @@ Licensed under the AVSystem-5-clause License. See the attached LICENSE file for details. -Bootstrapper (smart card bootstrap) -=================================== +Bootstrapper and SIM bootstrap +============================== .. contents:: :local: @@ -28,11 +28,21 @@ between Smartcard and LwM2M Device Storage in `Appendix H `_ thereof. -While communicating with the smart card is considered outside the scope for the -Anjay library, the "bootstrapper" feature, available commercially, implements a -parser for the file format described in `section G.3.4 of the Appendix G -`_ -mentioned above. +The "bootstrapper" feature, available as a commercial extension to the Anjay +library, includes two modules that aid in implementing this part of the +specification: + +* ``bootstrapper`` implements a parser for the file format described in + `section G.3.4 of the Appendix G + `_ + mentioned above +* ``sim_bootstrap`` implements the flow of `ISO/IEC 7816-4 + `_ commands + necessary to retrieve the aforementioned file + +With the above features in place, all that's left to implement is actual +communication with the smart card, typically sending and receiving ``AT+CSIM`` +commands to a cellular modem. Bootstrapping from smart card has a number of advantages, including: @@ -64,7 +74,42 @@ be used. The user will need to provide an implementation of ``avs_stream_t`` that allows the Anjay code to read the file contained on the smartcard. The ``avs_stream_simple_input_create()`` function from the `avs_stream_simple_io.h `_ -header is likely to be the easiest way to provide such an implementation. +header is likely to be the easiest way to provide such an implementation, aside +from using the SIM bootstrap module described below. + +Enabling and configuring the sim_bootstrap module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Similarly, to enable the sim_bootstrap module, you can enable the +``ANJAY_WITH_MODULE_SIM_BOOTSTRAP`` macro in the ``anjay_config.h`` file or, if +using CMake, enable the corresponding ``WITH_MODULE_sim_bootstrap`` CMake +option. This requires that the bootstrapper feature is also enabled. + +By default, the module will access the PKCS#15 application directory file and +search it for the EF(DODF-bootstrap) file in a way that is compliant with LwM2M +TS Appendix G mentioned above. + +However, you can override the OID of the file to look for, by defining the +``ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX`` macro in ``anjay_config.h`` +or setting the corresponding ``MODULE_sim_bootstrap_DATA_OID_OVERRIDE_HEX`` +CMake option. It shall be set to a string containing hexlified DER +representation of the desired OID. The default, standards-compliant value is +``"672b0901"`` (which corresponds to OID 2.23.43.9.1), but you may need to +change it to a different value, for example some cards are known to use a +mistakenly encoded value of ``"0604672b0901"``. + +Alternatively, you might define the +``ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID`` macro (or set the +``MODULE_sim_bootstrap_HARDCODED_FILE_ID`` CMake option) to bypass the directory +search entirely and set a hardcoded file ID, e.g. ``0x6432``. + +Once the module is enabled and configured, you can use the +`anjay_sim_bootstrap_stream_create() +<../api/sim__bootstrap_8h.html#a7cd497f30bfc7d36c6f0efb1db1d5a19>`_ function to +create an input stream suitable for passing to ``anjay_bootstrapper()``. In the +simplest case, you can also use the `anjay_sim_bootstrap_perform() +<../api/sim__bootstrap_8h.html#aa94114321f3af6532babde1efd9bdcec>`_ function +that combines both calls and automatically closes the stream as well. Bootstrap information generator tool ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -191,10 +236,11 @@ Example code commercial version of Anjay that includes the bootstrapper feature. The example is loosely based on the :doc:`../BasicClient/BC-MandatoryObjects` -tutorial. However, since the bootstrap information will be loaded from a file, -the ``setup_security_object()`` and ``setup_server_object()`` functions are no -longer necessary, and the calls to them can be replaced with direct calls to -`anjay_security_object_install() +tutorial, and additionally borrows much of the modem communication code from +:doc:`CF-NIDD`. Since the bootstrap information will be loaded from a smart +card, the ``setup_security_object()`` and ``setup_server_object()`` functions +are no longer necessary, and the calls to them can be replaced with direct calls +to `anjay_security_object_install() <../api/security_8h.html#a5fffaeedfc5c2933e58ac1446fd0401d>`_ and `anjay_server_object_install() <../api/server_8h.html#a36a369c0d7d1b2ad42c898ac47b75765>`_: @@ -205,8 +251,7 @@ longer necessary, and the calls to them can be replaced with direct calls to int main(int argc, char *argv[]) { if (argc != 3) { - avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME BOOTSTRAP_INFO_FILE", - argv[0]); + avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME MODEM_PATH", argv[0]); return -1; } @@ -231,7 +276,7 @@ longer necessary, and the calls to them can be replaced with direct calls to } if (!result) { - result = bootstrap_from_file(anjay, argv[2]); + result = bootstrap_from_sim(anjay, argv[2]); } if (!result) { @@ -246,40 +291,140 @@ longer necessary, and the calls to them can be replaced with direct calls to As you can see, the command line now expects a second argument with a name of the file containing the bootstrap information. -This file is loaded using the ``bootstrap_from_file()`` function, implemented as +This file is loaded using the ``bootstrap_from_sim()`` function, implemented as follows: .. highlight:: c .. snippet-source:: examples/commercial-features/CF-SmartCardBootstrap/src/main.c - static int bootstrap_from_file(anjay_t *anjay, const char *filename) { - avs_log(tutorial, INFO, "Attempting to bootstrap from file"); - - avs_stream_t *file_stream = - avs_stream_file_create(filename, AVS_STREAM_FILE_READ); - - if (!file_stream) { - avs_log(tutorial, ERROR, "Could not open file"); + typedef struct { + avs_buffer_t *buffer; + } fifo_t; + + // ... + + typedef struct { + fifo_t fifo; + int pts_fd; + } modem_ctx_t; + + // ... + + static int sim_perform_command(void *modem_ctx_, + const void *cmd, + size_t cmd_length, + void *out_buf, + size_t out_buf_size, + size_t *out_response_size) { + modem_ctx_t *modem_ctx = (modem_ctx_t *) modem_ctx_; + char req_buf[REQ_BUF_SIZE]; + char resp_buf[RESP_BUF_SIZE] = ""; + + char *req_buf_ptr = req_buf; + char *const req_buf_end = req_buf + sizeof(req_buf); + int result = avs_simple_snprintf(req_buf_ptr, + (size_t) (req_buf_end - req_buf_ptr), + "AT+CSIM=%" PRIu32 ",\"", + (uint32_t) (2 * cmd_length)); + if (result < 0) { + return result; + } + req_buf_ptr += result; + if ((size_t) (req_buf_end - req_buf_ptr) < 2 * cmd_length) { return -1; } - - int result = 0; - if (avs_is_err(anjay_bootstrapper(anjay, file_stream))) { - avs_log(tutorial, ERROR, "Could not bootstrap from file"); - result = -1; + if ((result = avs_hexlify(req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr), + NULL, cmd, cmd_length))) { + return result; + } + req_buf_ptr += 2 * cmd_length; + if ((result = avs_simple_snprintf( + req_buf_ptr, (size_t) (req_buf_end - req_buf_ptr), "\"\r\n")) + < 0) { + return result; + } + req_buf_ptr += result; + ssize_t written = + write(modem_ctx->pts_fd, req_buf, (size_t) (req_buf_ptr - req_buf)); + if (written != (ssize_t) (req_buf_ptr - req_buf)) { + return -1; } + avs_time_monotonic_t deadline = avs_time_monotonic_add( + avs_time_monotonic_now(), + avs_time_duration_from_scalar(5, AVS_TIME_S)); + bool csim_resp_received = false; + bool ok_received = false; + while (!ok_received) { + if (modem_getline(modem_ctx, resp_buf, sizeof(resp_buf), deadline)) { + return -1; + } + const char *resp_terminator = memchr(resp_buf, '\0', sizeof(resp_buf)); + if (!resp_terminator) { + return -1; + } + if (memcmp(resp_buf, CSIM_RESP, strlen(CSIM_RESP)) == 0) { + if (csim_resp_received) { + return -1; + } + errno = 0; + char *endptr = NULL; + long long resp_reported_length = + strtoll(resp_buf + strlen(CSIM_RESP), &endptr, 10); + if (errno || !endptr || endptr[0] != ',' || endptr[1] != '"' + || resp_reported_length < 0 + || endptr + resp_reported_length + 2 >= resp_terminator + || endptr[resp_reported_length + 2] != '"' + || avs_unhexlify(out_response_size, (uint8_t *) out_buf, + out_buf_size, endptr + 2, + (size_t) resp_reported_length)) { + return -1; + } + csim_resp_received = true; + } else if (strcmp(resp_buf, "OK") == 0) { + ok_received = true; + } + } + return csim_resp_received ? 0 : -1; + } + + static int bootstrap_from_sim(anjay_t *anjay, const char *modem_device) { + modem_ctx_t modem_ctx = { + .pts_fd = -1 + }; + int result = -1; + + avs_log(tutorial, INFO, "Attempting to bootstrap from SIM card"); - avs_stream_cleanup(&file_stream); + if (fifo_init(&modem_ctx.fifo)) { + avs_log(tutorial, ERROR, "could not initialize FIFO"); + goto finish; + } + if ((modem_ctx.pts_fd = open(modem_device, O_RDWR)) < 0) { + avs_log(tutorial, ERROR, "could not open modem device %s: %s", + modem_device, strerror(errno)); + goto finish; + } + if (avs_is_err(anjay_sim_bootstrap_perform(anjay, sim_perform_command, + &modem_ctx))) { + avs_log(tutorial, ERROR, "Could not bootstrap from SIM card"); + goto finish; + } + result = 0; + finish: + if (modem_ctx.pts_fd >= 0) { + close(modem_ctx.pts_fd); + } + fifo_destroy(&modem_ctx.fifo); return result; } -This shares similarities with the ``restore_objects_if_possible()`` function -from the :doc:`../AdvancedTopics/AT-Persistence` tutorial. +The ``sim_perform_command()`` function is a callback that is passed to the +``sim_bootstrap`` module logic, and performs the ``AT+CSIM`` command over a +serial port. The ``modem_getline()`` function it calls is almost identical to +the one originally implemented for :doc:`CF-NIDD`. -As mentioned in the :ref:`cf-smart-card-bootstrap-enabling` section above, to -perform bootstrap using an actual smart card, the -``avs_stream_simple_input_create()`` function from the `avs_stream_simple_io.h -`_ -header could be used instead of the ``avs_stream_file_create()`` call that is -used here to access regular file system. +The ``bootstrap_from_sim()`` function itself is a wrapper over +`anjay_sim_bootstrap_perform() +<../api/sim__bootstrap_8h.html#aa94114321f3af6532babde1efd9bdcec>`_ that +additionally initializes and closes the card communication channel. diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css index c718cee4..4d153448 100644 --- a/_static/css/badge_only.css +++ b/_static/css/badge_only.css @@ -1 +1 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:normal;src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2") format("woff2"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60}.rst-versions .rst-current-version::after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge .fa-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff b/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb60000..00000000 Binary files a/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff2 b/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e231..00000000 Binary files a/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff b/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63f..00000000 Binary files a/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff2 b/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5b..00000000 Binary files a/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/_static/css/fonts/lato-bold-italic.woff b/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b9..00000000 Binary files a/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/_static/css/fonts/lato-bold-italic.woff2 b/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d804..00000000 Binary files a/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/_static/css/fonts/lato-bold.woff b/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51f..00000000 Binary files a/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/_static/css/fonts/lato-bold.woff2 b/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb195043..00000000 Binary files a/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/_static/css/fonts/lato-normal-italic.woff b/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc0..00000000 Binary files a/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/_static/css/fonts/lato-normal-italic.woff2 b/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37e..00000000 Binary files a/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/_static/css/fonts/lato-normal.woff b/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307ff..00000000 Binary files a/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/_static/css/fonts/lato-normal.woff2 b/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf98433..00000000 Binary files a/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/_static/css/theme.css b/_static/css/theme.css index 09a1af86..40606a86 100644 --- a/_static/css/theme.css +++ b/_static/css/theme.css @@ -1,4 +1,4 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! +html{box-sizing:border-box}*,*::after,*::before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,.rst-content .toctree-wrapper>p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper>p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dt:after,html.writer-html5 .rst-content dl.field-list>dt:after,html.writer-html5 .rst-content dl.footnote>dt:after{content:":"}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets,html.writer-html5 .rst-content dl.footnote>dt>span.brackets{margin-right:.5rem}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{font-style:italic}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p,html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content dl.citation,.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content dl.citation code,.rst-content dl.citation tt,.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.current>a button.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.wy-menu-vertical li button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.rst-content .fa-pull-left.admonition-title,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content dl dt .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.rst-content code.download span.fa-pull-left:first-child,.fa-pull-left.icon{margin-right:.3em}.fa.fa-pull-right,.wy-menu-vertical li button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.rst-content .fa-pull-right.admonition-title,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content dl dt .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.rst-content code.download span.fa-pull-right:first-child,.fa-pull-right.icon{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li.current>a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.current>a button.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.current>a button.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p .headerlink,.rst-content p a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content table>caption .headerlink,.rst-content table>caption a .headerlink,a .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption a .headerlink,a .rst-content .eqno .headerlink,.rst-content .eqno a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .btn button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.btn .wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p .headerlink,.rst-content p .btn .headerlink,.btn .rst-content table>caption .headerlink,.rst-content table>caption .btn .headerlink,.btn .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .btn .headerlink,.btn .rst-content .eqno .headerlink,.rst-content .eqno .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand,.nav .wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p .headerlink,.rst-content p .nav .headerlink,.nav .rst-content table>caption .headerlink,.rst-content table>caption .nav .headerlink,.nav .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .nav .headerlink,.nav .rst-content .eqno .headerlink,.rst-content .eqno .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.btn .rst-content .code-block-caption .fa-large.headerlink,.rst-content .code-block-caption .btn .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.nav .rst-content .code-block-caption .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.btn .rst-content .code-block-caption .fa-spin.headerlink,.rst-content .code-block-caption .btn .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.nav .rst-content .code-block-caption .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li button.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p .headerlink:before,.rst-content p .btn-mini .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.admonition{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo,.rst-content .wy-alert-warning.admonition{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title,.rst-content .wy-alert-warning.admonition .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.admonition{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.admonition{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.admonition{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 .3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.3576520234%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.3576520234%;width:48.8211739883%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.3576520234%;width:31.7615653177%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type="datetime-local"]{padding:.34375em .625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9 ;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{position:absolute;content:"";display:block;left:0;top:0;width:36px;height:12px;border-radius:4px;background:#ccc;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27AE60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:.3em;display:block}.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper>p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper>p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content .section ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content .section ol.arabic li,.rst-content section ol li,.rst-content section ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content .toctree-wrapper ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content section ol li p:last-child,.rst-content .toctree-wrapper ol li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content .section ol.arabic li ul,.rst-content section ol li ul,.rst-content section ol.arabic li ul,.rst-content .toctree-wrapper ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content .section ol.arabic li ul li,.rst-content section ol li ul li,.rst-content section ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:before,.wy-breadcrumbs:after{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0 0;display:block;font-weight:bold;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover button.toctree-expand,.wy-menu-vertical li.current>a:hover button.toctree-expand{color:gray}.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li.current>a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 4.045em;padding-right:1.618em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 5.663em;padding-right:1.618em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 7.281em;padding-right:1.618em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 8.899em;padding-right:1.618em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 10.517em;padding-right:1.618em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 12.135em;padding-right:1.618em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 13.753em;padding-right:1.618em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 15.371em;padding-right:1.618em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 16.989em;padding-right:1.618em}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:normal}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980B9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:gray}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:gray}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{width:100%}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:before,.rst-breadcrumbs-buttons:after{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1100px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content h1,.rst-content h2,.rst-content .toctree-wrapper>p.caption,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0px}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img,.rst-content section>img,.rst-content section>a>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;display:block;overflow:auto}.rst-content pre.literal-block,.rst-content div[class^='highlight']{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px 0}.rst-content pre.literal-block div[class^='highlight'],.rst-content div[class^='highlight'] div[class^='highlight']{padding:0px;border:none;margin:0}.rst-content div[class^='highlight'] td.code{width:100%}.rst-content .linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;display:block;overflow:auto}.rst-content div[class^='highlight'] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content pre.literal-block,.rst-content div[class^='highlight'] pre,.rst-content .linenodiv pre{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight span.linenos,.rst-content div.highlight .gp{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0px;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^='highlight'],.rst-content div[class^='highlight'] pre{white-space:pre-wrap}}.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition{clear:both}.rst-content .note .last,.rst-content .note>*:last-child,.rst-content .attention .last,.rst-content .attention>*:last-child,.rst-content .caution .last,.rst-content .caution>*:last-child,.rst-content .danger .last,.rst-content .danger>*:last-child,.rst-content .error .last,.rst-content .error>*:last-child,.rst-content .hint .last,.rst-content .hint>*:last-child,.rst-content .important .last,.rst-content .important>*:last-child,.rst-content .tip .last,.rst-content .tip>*:last-child,.rst-content .warning .last,.rst-content .warning>*:last-child,.rst-content .seealso .last,.rst-content .seealso>*:last-child,.rst-content .admonition-todo .last,.rst-content .admonition-todo>*:last-child,.rst-content .admonition .last,.rst-content .admonition>*:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content section ol li>*,.rst-content section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>*:first-child,.rst-content .section ul li>*:first-child,.rst-content section ol li>*:first-child,.rst-content section ul li>*:first-child,.rst-content .toctree-wrapper ol li>*:first-child,.rst-content .toctree-wrapper ul li>*:first-child{margin-top:0rem}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child{margin-bottom:0rem}.rst-content .section ol li>ul,.rst-content .section ol li>ol,.rst-content .section ul li>ul,.rst-content .section ul li>ol,.rst-content section ol li>ul,.rst-content section ol li>ol,.rst-content section ul li>ul,.rst-content section ul li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content .toctree-wrapper ul li>ol{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ul.simple li>*,.rst-content section ol.simple li>*,.rst-content section ul.simple li>*,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ul.simple li>*{margin-top:0rem;margin-bottom:0rem}.rst-content .section ol.simple li ul,.rst-content .section ol.simple li ol,.rst-content .section ul.simple li ul,.rst-content .section ul.simple li ol,.rst-content section ol.simple li ul,.rst-content section ol.simple li ol,.rst-content section ul.simple li ul,.rst-content section ul.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content .toctree-wrapper ul.simple li ol{margin-top:0rem;margin-bottom:0rem}.rst-content .line-block{margin-left:0px;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content h1 .headerlink:focus,.rst-content h2 .headerlink:focus,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content h3 .headerlink:focus,.rst-content h4 .headerlink:focus,.rst-content h5 .headerlink:focus,.rst-content h6 .headerlink:focus,.rst-content dl dt .headerlink:focus,.rst-content p .headerlink:focus,.rst-content p.caption .headerlink:focus,.rst-content table>caption .headerlink:focus,.rst-content .code-block-caption .headerlink:focus,.rst-content .eqno .headerlink:focus{opacity:1}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p:hover .headerlink,.rst-content p.caption:hover .headerlink,.rst-content table>caption:hover .headerlink,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno:hover .headerlink{opacity:1}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>*:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;box-shadow:0 0 0 2px #F1C40F;display:inline;font-weight:bold}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:baseline;position:relative;top:-0.4em;line-height:0;font-size:90%}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none !important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.footnote,html.writer-html5 .rst-content dl.field-list{display:grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content dl.footnote>dt,html.writer-html5 .rst-content dl.field-list>dt{padding-left:1rem}html.writer-html5 .rst-content dl.footnote>dt:after,html.writer-html5 .rst-content dl.field-list>dt:after{content:":"}html.writer-html5 .rst-content dl.footnote>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.field-list>dd{margin-bottom:0rem}html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.footnote>dt{margin:0rem .5rem .5rem 0rem;line-height:1.2rem;word-break:break-all;font-weight:normal}html.writer-html5 .rst-content dl.footnote>dt>span.brackets{margin-right:.5rem}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{font-style:italic}html.writer-html5 .rst-content dl.footnote>dd{margin:0rem 0rem .5rem 0rem;line-height:1.2rem}html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}html.writer-html4 .rst-content table.docutils.citation,.rst-content table.docutils.footnote,html.writer-html5 .rst-content dl.footnote{color:gray}html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html4 .rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code,html.writer-html5 .rst-content dl.footnote tt,html.writer-html5 .rst-content dl.footnote code{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils th>p,html.writer-html5 .rst-content table.docutils td>p{line-height:1rem;margin-bottom:0rem;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>*:last-child{margin-bottom:0}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list td p{font-size:inherit;line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content tt,.rst-content tt,.rst-content code{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C;white-space:normal}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content pre,.rst-content kbd,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold;margin-bottom:12px}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100% !important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dl:not(.field-list)>dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dl:not(.field-list)>dt .headerlink{color:#404040;font-size:100% !important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descname{font-weight:bold}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .descclassname{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;color:#000}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-weight:normal;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child,.rst-content code.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .versionmodified{font-style:italic}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Lato";src:url("../fonts/Lato-Regular.woff2") format("woff2"),url("../fonts/Lato-Regular.ttf") format("truetype");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:"Lato";src:url("../fonts/Lato-Bold.woff2") format("woff2"),url("../fonts/Lato-Bold.ttf") format("truetype");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:"Lato";src:url("../fonts/Lato-BoldItalic.woff2") format("woff2"),url("../fonts/Lato-BoldItalic.ttf") format("truetype");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:"Lato";src:url("../fonts/Lato-Italic.woff2") format("woff2"),url("../fonts/Lato-Italic.ttf") format("truetype");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:url("../fonts/RobotoSlab-Regular.woff2") format("woff2");font-display:block}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:url("../fonts/RobotoSlab-Bold.woff2") format("woff2");font-display:block} diff --git a/_static/documentation_options.js b/_static/documentation_options.js index a281c2e4..cd40594e 100644 --- a/_static/documentation_options.js +++ b/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '3.4.0', + VERSION: '3.4.1', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/_static/fonts/Lato-Bold.ttf b/_static/fonts/Lato-Bold.ttf new file mode 100644 index 00000000..70c4dd92 Binary files /dev/null and b/_static/fonts/Lato-Bold.ttf differ diff --git a/_static/fonts/Lato-Bold.woff2 b/_static/fonts/Lato-Bold.woff2 new file mode 100644 index 00000000..2ab3f6de Binary files /dev/null and b/_static/fonts/Lato-Bold.woff2 differ diff --git a/_static/fonts/Lato-BoldItalic.ttf b/_static/fonts/Lato-BoldItalic.ttf new file mode 100644 index 00000000..c0e84bc7 Binary files /dev/null and b/_static/fonts/Lato-BoldItalic.ttf differ diff --git a/_static/fonts/Lato-BoldItalic.woff2 b/_static/fonts/Lato-BoldItalic.woff2 new file mode 100644 index 00000000..3cedab63 Binary files /dev/null and b/_static/fonts/Lato-BoldItalic.woff2 differ diff --git a/_static/fonts/Lato-Italic.ttf b/_static/fonts/Lato-Italic.ttf new file mode 100644 index 00000000..e7a31ce3 Binary files /dev/null and b/_static/fonts/Lato-Italic.ttf differ diff --git a/_static/fonts/Lato-Italic.woff2 b/_static/fonts/Lato-Italic.woff2 new file mode 100644 index 00000000..005bd62b Binary files /dev/null and b/_static/fonts/Lato-Italic.woff2 differ diff --git a/_static/fonts/Lato-Regular.ttf b/_static/fonts/Lato-Regular.ttf new file mode 100644 index 00000000..b536f955 Binary files /dev/null and b/_static/fonts/Lato-Regular.ttf differ diff --git a/_static/fonts/Lato-Regular.woff2 b/_static/fonts/Lato-Regular.woff2 new file mode 100644 index 00000000..597115a0 Binary files /dev/null and b/_static/fonts/Lato-Regular.woff2 differ diff --git a/_static/fonts/RobotoSlab-Bold.woff2 b/_static/fonts/RobotoSlab-Bold.woff2 new file mode 100644 index 00000000..40a6cbc8 Binary files /dev/null and b/_static/fonts/RobotoSlab-Bold.woff2 differ diff --git a/_static/fonts/RobotoSlab-Regular.woff2 b/_static/fonts/RobotoSlab-Regular.woff2 new file mode 100644 index 00000000..d36556f2 Binary files /dev/null and b/_static/fonts/RobotoSlab-Regular.woff2 differ diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/fonts/fontawesome-webfont.eot similarity index 100% rename from _static/css/fonts/fontawesome-webfont.eot rename to _static/fonts/fontawesome-webfont.eot diff --git a/_static/css/fonts/fontawesome-webfont.svg b/_static/fonts/fontawesome-webfont.svg similarity index 100% rename from _static/css/fonts/fontawesome-webfont.svg rename to _static/fonts/fontawesome-webfont.svg diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/fonts/fontawesome-webfont.ttf similarity index 100% rename from _static/css/fonts/fontawesome-webfont.ttf rename to _static/fonts/fontawesome-webfont.ttf diff --git a/_static/css/fonts/fontawesome-webfont.woff b/_static/fonts/fontawesome-webfont.woff similarity index 100% rename from _static/css/fonts/fontawesome-webfont.woff rename to _static/fonts/fontawesome-webfont.woff diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/fonts/fontawesome-webfont.woff2 similarity index 100% rename from _static/css/fonts/fontawesome-webfont.woff2 rename to _static/fonts/fontawesome-webfont.woff2 diff --git a/_static/js/badge_only.js b/_static/js/badge_only.js deleted file mode 100644 index 526d7234..00000000 --- a/_static/js/badge_only.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); \ No newline at end of file diff --git a/_static/js/html5shiv-printshiv.min.js b/_static/js/html5shiv-printshiv.min.js deleted file mode 100644 index 2b43bd06..00000000 --- a/_static/js/html5shiv-printshiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/html5shiv.min.js b/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674f..00000000 --- a/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/theme.js b/_static/js/theme.js index 1fddb6ee..2defd10d 100644 --- a/_static/js/theme.js +++ b/_static/js/theme.js @@ -1 +1,265 @@ -!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
      "),n("table.docutils.footnote").wrap("
      "),n("table.docutils.citation").wrap("
      "),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t
      "); + + // Add extra class to responsive tables that contain + // footnotes or citations so that we can target them for styling + $("table.docutils.footnote") + .wrap("
      "); + $("table.docutils.citation") + .wrap("
      "); + + // Add expand links to all parents of nested ul + $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { + var link = $(this); + expand = + $(''); + expand.on('click', function (ev) { + self.toggleCurrent(link); + ev.stopPropagation(); + return false; + }); + link.prepend(expand); + }); + }; + + nav.reset = function () { + // Get anchor from URL and open up nested nav + var anchor = encodeURI(window.location.hash) || '#'; + + try { + var vmenu = $('.wy-menu-vertical'); + var link = vmenu.find('[href="' + anchor + '"]'); + if (link.length === 0) { + // this link was not found in the sidebar. + // Find associated id element, then its closest section + // in the document and try with that one. + var id_elt = $('.document [id="' + anchor.substring(1) + '"]'); + var closest_section = id_elt.closest('div.section'); + link = vmenu.find('[href="#' + closest_section.attr("id") + '"]'); + if (link.length === 0) { + // still not found in the sidebar. fall back to main section + link = vmenu.find('[href="#"]'); + } + } + // If we found a matching link then reset current and re-apply + // otherwise retain the existing match + if (link.length > 0) { + $('.wy-menu-vertical .current') + .removeClass('current') + .attr('aria-expanded','false'); + link.addClass('current') + .attr('aria-expanded','true'); + link.closest('li.toctree-l1') + .parent() + .addClass('current') + .attr('aria-expanded','true'); + for (let i = 1; i <= 10; i++) { + link.closest('li.toctree-l' + i) + .addClass('current') + .attr('aria-expanded','true'); + } + link[0].scrollIntoView(); + } + } + catch (err) { + console.log("Error expanding nav for anchor", err); + } + + }; + + nav.onScroll = function () { + this.winScroll = false; + var newWinPosition = this.win.scrollTop(), + winBottom = newWinPosition + this.winHeight, + navPosition = this.navBar.scrollTop(), + newNavPosition = navPosition + (newWinPosition - this.winPosition); + if (newWinPosition < 0 || winBottom > this.docHeight) { + return; + } + this.navBar.scrollTop(newNavPosition); + this.winPosition = newWinPosition; + }; + + nav.onResize = function () { + this.winResize = false; + this.winHeight = this.win.height(); + this.docHeight = $(document).height(); + }; + + nav.hashChange = function () { + this.linkScroll = true; + this.win.one('hashchange', function () { + this.linkScroll = false; + }); + }; + + nav.toggleCurrent = function (elem) { + var parent_li = elem.closest('li'); + parent_li + .siblings('li.current') + .removeClass('current') + .attr('aria-expanded','false'); + parent_li + .siblings() + .find('li.current') + .removeClass('current') + .attr('aria-expanded','false'); + var children = parent_li.find('> ul li'); + // Don't toggle terminal elements. + if (children.length) { + children + .removeClass('current') + .attr('aria-expanded','false'); + parent_li + .toggleClass('current') + .attr('aria-expanded', function(i, old) { + return old == 'true' ? 'false' : 'true'; + }); + } + } + + return nav; +}; + +_ThemeNav = ThemeNav(); + +if (typeof(window) != 'undefined') { + window.SphinxRtdTheme = { + Navigation: _ThemeNav, + // TODO remove this once static assets are split up between the theme + // and Read the Docs. For now, this patches 0.3.0 to be backwards + // compatible with a pre-0.3.0 layout.html + StickyNav: _ThemeNav, + }; +} + + +// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel +// https://gist.github.com/paulirish/1579671 +// MIT license + +(function() { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] + || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + + if (!window.cancelAnimationFrame) + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; +}()); diff --git a/api/access__control_8h.html b/api/access__control_8h.html index 8df15109..78e41222 100644 --- a/api/access__control_8h.html +++ b/api/access__control_8h.html @@ -383,7 +383,7 @@

      diff --git a/api/access__control_8h_source.html b/api/access__control_8h_source.html index 44cca1ef..f1a917b4 100644 --- a/api/access__control_8h_source.html +++ b/api/access__control_8h_source.html @@ -153,7 +153,7 @@ diff --git a/api/advanced__fw__update_8h.html b/api/advanced__fw__update_8h.html index 3ac64446..fdb13100 100644 --- a/api/advanced__fw__update_8h.html +++ b/api/advanced__fw__update_8h.html @@ -1244,7 +1244,7 @@

      diff --git a/api/advanced__fw__update_8h_source.html b/api/advanced__fw__update_8h_source.html index 8bda2099..c42d6971 100644 --- a/api/advanced__fw__update_8h_source.html +++ b/api/advanced__fw__update_8h_source.html @@ -364,7 +364,7 @@ diff --git a/api/anjay_8h.html b/api/anjay_8h.html index e54a4ce8..dad5a10b 100644 --- a/api/anjay_8h.html +++ b/api/anjay_8h.html @@ -97,7 +97,7 @@ diff --git a/api/anjay_8h_source.html b/api/anjay_8h_source.html index a9b54853..0b8e9965 100644 --- a/api/anjay_8h_source.html +++ b/api/anjay_8h_source.html @@ -113,7 +113,7 @@ diff --git a/api/anjay__config_8h.html b/api/anjay__config_8h.html index 6ab8d7af..3e1b17d2 100644 --- a/api/anjay__config_8h.html +++ b/api/anjay__config_8h.html @@ -185,6 +185,8 @@   #define ANJAY_WITH_MODULE_BOOTSTRAPPER   +#define ANJAY_WITH_MODULE_SIM_BOOTSTRAP +  #define ANJAY_WITH_MODULE_FACTORY_PROVISIONING   @@ -723,7 +725,15 @@

      -

      Enable factory provisioning module. Data provided during provisioning uses SenML CBOR format.

      +

      Forced ID of the file to read the SIM bootstrap information from.

      +

      If not defined (default), the bootstrap information file will be discovered through the ODF file, as mandated by the specification.

      +

      Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled. At most one of ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID and ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX may be defined at the same time.

      +

      IMPORTANT: Only available with the bootstrapper feature. Ignored in the open source version. Overridden OID of the SIM bootstrap information to look for in the DODF file, expressed as a hexlified DER representation (without the header).

      +

      This is the hexlified expected value of the 'id' field within the 'OidDO' sequence in the DODF file (please refer to the PKCS #15 document for more information).

      +

      If not defined, the default value of "672b0901", which corresponds to OID 2.23.43.9.1 {joint-iso-itu-t(2) international-organizations(23) wap(43) oma-lwm2m(9) lwm2m-bootstrap(1)}, will be used.

      +

      No other values than the default are valid according to the specification, but some SIM cards are known to use other non-standard values, e.g. "0604672b0901" - including a superfluous nested BER-TLV header, as erroneously illustrated in the EF(DODF-bootstrap) file coding example in LwM2M TS 1.2 and earlier (fixed in LwM2M TS 1.2.1) - which is interpreted as OID 0.6.4.103.43.9.1 (note that it is invalid as the 0.6 tree does not exist in the repository as of writing this note).

      +

      Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled. At most one of ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID and ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX may be defined at the same time.

      +

      IMPORTANT: Only available with the bootstrapper feature. Ignored in the open source version. Enable factory provisioning module. Data provided during provisioning uses SenML CBOR format.

      @@ -804,6 +814,23 @@

      Enable server module (implementation of the LwM2M Server object).

      +

      +
      + +

      ◆ ANJAY_WITH_MODULE_SIM_BOOTSTRAP

      + +
      +
      + + + + +
      #define ANJAY_WITH_MODULE_SIM_BOOTSTRAP
      +
      +

      Enable the SIM bootstrap module, which enables reading the SIM bootstrap information from a smartcard, which can then be passed through to the bootstrapper module.

      +

      Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled.

      +

      IMPORTANT: Only available with the bootstrapper feature. Ignored in the open source version.

      +
      @@ -994,7 +1021,7 @@

      diff --git a/api/anjay__config_8h.js b/api/anjay__config_8h.js index 60022a44..43deab01 100644 --- a/api/anjay__config_8h.js +++ b/api/anjay__config_8h.js @@ -36,6 +36,7 @@ var anjay__config_8h = [ "ANJAY_WITH_MODULE_SECURITY", "anjay__config_8h.html#af3826c0f2b7e58651d8237f2d8f71394", null ], [ "ANJAY_WITH_MODULE_SECURITY_ENGINE_SUPPORT", "anjay__config_8h.html#a73efb35fc555fdda695dc571626850e6", null ], [ "ANJAY_WITH_MODULE_SERVER", "anjay__config_8h.html#af53296d4db302f03403cdf2b0443b59a", null ], + [ "ANJAY_WITH_MODULE_SIM_BOOTSTRAP", "anjay__config_8h.html#ac5fb9b290dc4936eb045ad850de10b96", null ], [ "ANJAY_WITH_NET_STATS", "anjay__config_8h.html#a16f03e937cee569e516ff4616c3b97fc", null ], [ "ANJAY_WITH_NIDD", "anjay__config_8h.html#a587ff02d54991baf464a4b911612b25a", null ], [ "ANJAY_WITH_OBSERVATION_STATUS", "anjay__config_8h.html#a94b3894de8e54ba40684218883475ac2", null ], diff --git a/api/anjay__config_8h_source.html b/api/anjay__config_8h_source.html index 03b6a8cf..72ba4df8 100644 --- a/api/anjay__config_8h_source.html +++ b/api/anjay__config_8h_source.html @@ -205,17 +205,23 @@
      550 
      563 #define ANJAY_WITH_MODULE_BOOTSTRAPPER
      564 
      -
      569 #define ANJAY_WITH_MODULE_FACTORY_PROVISIONING
      -
      570 
      -
      577 /* #undef ANJAY_WITH_MODULE_OSCORE */
      -
      580 #endif // ANJAY_CONFIG_H
      +
      575 #define ANJAY_WITH_MODULE_SIM_BOOTSTRAP
      +
      576 
      +
      591 /* #undef ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID */
      +
      592 
      +
      621 /* #undef ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX */
      +
      622 
      +
      627 #define ANJAY_WITH_MODULE_FACTORY_PROVISIONING
      +
      628 
      +
      635 /* #undef ANJAY_WITH_MODULE_OSCORE */
      +
      638 #endif // ANJAY_CONFIG_H
      diff --git a/api/annotated.html b/api/annotated.html index 8ed20940..f1a983c6 100644 --- a/api/annotated.html +++ b/api/annotated.html @@ -123,7 +123,7 @@ diff --git a/api/at__sms_8h.html b/api/at__sms_8h.html index 351f684b..2bc6f4e8 100644 --- a/api/at__sms_8h.html +++ b/api/at__sms_8h.html @@ -132,7 +132,7 @@

        - +

      diff --git a/api/at__sms_8h_source.html b/api/at__sms_8h_source.html index 13c0b4c7..9c68e467 100644 --- a/api/at__sms_8h_source.html +++ b/api/at__sms_8h_source.html @@ -118,7 +118,7 @@ diff --git a/api/attr__storage_8h.html b/api/attr__storage_8h.html index e8f1c7a8..977d96d1 100644 --- a/api/attr__storage_8h.html +++ b/api/attr__storage_8h.html @@ -499,7 +499,7 @@

      diff --git a/api/attr__storage_8h_source.html b/api/attr__storage_8h_source.html index a8634a9e..d99c54d6 100644 --- a/api/attr__storage_8h_source.html +++ b/api/attr__storage_8h_source.html @@ -171,7 +171,7 @@ diff --git a/api/bg96__nidd_8h.html b/api/bg96__nidd_8h.html index 5dcd8ae2..c304976c 100644 --- a/api/bg96__nidd_8h.html +++ b/api/bg96__nidd_8h.html @@ -247,7 +247,7 @@

      diff --git a/api/bg96__nidd_8h_source.html b/api/bg96__nidd_8h_source.html index 58883cc6..aa19e878 100644 --- a/api/bg96__nidd_8h_source.html +++ b/api/bg96__nidd_8h_source.html @@ -167,7 +167,7 @@ diff --git a/api/bootstrapper_8h.html b/api/bootstrapper_8h.html index 8019b8e4..9967d640 100644 --- a/api/bootstrapper_8h.html +++ b/api/bootstrapper_8h.html @@ -141,7 +141,7 @@

      diff --git a/api/bootstrapper_8h_source.html b/api/bootstrapper_8h_source.html index b2fa9fcd..87fd94e8 100644 --- a/api/bootstrapper_8h_source.html +++ b/api/bootstrapper_8h_source.html @@ -120,7 +120,7 @@ diff --git a/api/classes.html b/api/classes.html index bfc19f77..f20cd6d6 100644 --- a/api/classes.html +++ b/api/classes.html @@ -96,7 +96,7 @@ diff --git a/api/core_8h.html b/api/core_8h.html index 9d46c261..d8a9c6f8 100644 --- a/api/core_8h.html +++ b/api/core_8h.html @@ -3029,7 +3029,7 @@

        - +

      diff --git a/api/core_8h_source.html b/api/core_8h_source.html index a376cc01..369869cd 100644 --- a/api/core_8h_source.html +++ b/api/core_8h_source.html @@ -710,7 +710,7 @@ diff --git a/api/dir_ae037c79f53c15c524ca38a0620e930a.html b/api/dir_ae037c79f53c15c524ca38a0620e930a.html index 169e982c..c1258cdb 100644 --- a/api/dir_ae037c79f53c15c524ca38a0620e930a.html +++ b/api/dir_ae037c79f53c15c524ca38a0620e930a.html @@ -97,7 +97,7 @@ diff --git a/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.html b/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.html index 3e9cd26d..8aa15c26 100644 --- a/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.html +++ b/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.html @@ -126,6 +126,8 @@   file  server.h [code]   +file  sim_bootstrap.h [code] +  file  sms.h [code]   file  stats.h [code] @@ -137,7 +139,7 @@ diff --git a/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.js b/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.js index a7fcdff6..cd0f0e80 100644 --- a/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.js +++ b/api/dir_f1282f3667e08fb5f3f6a37ec99711a1.js @@ -19,6 +19,7 @@ var dir_f1282f3667e08fb5f3f6a37ec99711a1 = [ "nidd.h", "nidd_8h.html", "nidd_8h" ], [ "security.h", "security_8h.html", "security_8h" ], [ "server.h", "server_8h.html", "server_8h" ], + [ "sim_bootstrap.h", "sim__bootstrap_8h.html", "sim__bootstrap_8h" ], [ "sms.h", "sms_8h.html", "sms_8h" ], [ "stats.h", "stats_8h.html", "stats_8h" ] ]; \ No newline at end of file diff --git a/api/dm_8h.html b/api/dm_8h.html index 7acdc89e..5c464edb 100644 --- a/api/dm_8h.html +++ b/api/dm_8h.html @@ -1570,7 +1570,7 @@

        - +

      diff --git a/api/dm_8h_source.html b/api/dm_8h_source.html index 5a302199..a2a3e0f7 100644 --- a/api/dm_8h_source.html +++ b/api/dm_8h_source.html @@ -523,7 +523,7 @@ diff --git a/api/download_8h.html b/api/download_8h.html index 83820b36..e7196bb5 100644 --- a/api/download_8h.html +++ b/api/download_8h.html @@ -486,7 +486,7 @@

      diff --git a/api/download_8h_source.html b/api/download_8h_source.html index ba6cc098..4e9b0304 100644 --- a/api/download_8h_source.html +++ b/api/download_8h_source.html @@ -229,7 +229,7 @@ diff --git a/api/factory__provisioning_8h.html b/api/factory__provisioning_8h.html index 7366801c..73cc859b 100644 --- a/api/factory__provisioning_8h.html +++ b/api/factory__provisioning_8h.html @@ -149,7 +149,7 @@

      diff --git a/api/factory__provisioning_8h_source.html b/api/factory__provisioning_8h_source.html index 5756e412..c99cb69b 100644 --- a/api/factory__provisioning_8h_source.html +++ b/api/factory__provisioning_8h_source.html @@ -121,7 +121,7 @@ diff --git a/api/files.html b/api/files.html index a175e431..7f558fc3 100644 --- a/api/files.html +++ b/api/files.html @@ -108,8 +108,9 @@  nidd.h  security.h  server.h - sms.h - stats.h + sim_bootstrap.h + sms.h + stats.h
      @@ -117,7 +118,7 @@ diff --git a/api/functions.html b/api/functions.html index 5fd1021e..f9ebc5bf 100644 --- a/api/functions.html +++ b/api/functions.html @@ -93,7 +93,7 @@

      - a -

        diff --git a/api/functions_b.html b/api/functions_b.html index 3c69d2b7..5619787a 100644 --- a/api/functions_b.html +++ b/api/functions_b.html @@ -102,7 +102,7 @@

        - b -

          diff --git a/api/functions_c.html b/api/functions_c.html index 958eb485..ccf4d7fc 100644 --- a/api/functions_c.html +++ b/api/functions_c.html @@ -141,7 +141,7 @@

          - c -

            diff --git a/api/functions_d.html b/api/functions_d.html index 25533408..bd76ed2e 100644 --- a/api/functions_d.html +++ b/api/functions_d.html @@ -117,7 +117,7 @@

            - d -

              diff --git a/api/functions_e.html b/api/functions_e.html index 2678cb0d..c108dba4 100644 --- a/api/functions_e.html +++ b/api/functions_e.html @@ -123,7 +123,7 @@

              - e -

                diff --git a/api/functions_f.html b/api/functions_f.html index 4761a549..f194bf74 100644 --- a/api/functions_f.html +++ b/api/functions_f.html @@ -94,7 +94,7 @@

                - f -

                  diff --git a/api/functions_g.html b/api/functions_g.html index 471239e4..ab7939e9 100644 --- a/api/functions_g.html +++ b/api/functions_g.html @@ -126,7 +126,7 @@

                  - g -

                    diff --git a/api/functions_h.html b/api/functions_h.html index b3c65b78..92a4e485 100644 --- a/api/functions_h.html +++ b/api/functions_h.html @@ -93,7 +93,7 @@

                    - h -

                      diff --git a/api/functions_i.html b/api/functions_i.html index 56adae8a..7c2f6ded 100644 --- a/api/functions_i.html +++ b/api/functions_i.html @@ -118,7 +118,7 @@

                      - i -

                        diff --git a/api/functions_l.html b/api/functions_l.html index 219ef900..bfa95ae5 100644 --- a/api/functions_l.html +++ b/api/functions_l.html @@ -111,7 +111,7 @@

                        - l -

                          diff --git a/api/functions_m.html b/api/functions_m.html index 8015e371..2f1aabaf 100644 --- a/api/functions_m.html +++ b/api/functions_m.html @@ -139,7 +139,7 @@

                          - m -

                            diff --git a/api/functions_n.html b/api/functions_n.html index 2a8a3e74..792ed3f4 100644 --- a/api/functions_n.html +++ b/api/functions_n.html @@ -105,7 +105,7 @@

                            - n -

                              diff --git a/api/functions_o.html b/api/functions_o.html index a2321040..f53261e7 100644 --- a/api/functions_o.html +++ b/api/functions_o.html @@ -113,7 +113,7 @@

                              - o -

                                diff --git a/api/functions_p.html b/api/functions_p.html index 81b2d403..8a16bca2 100644 --- a/api/functions_p.html +++ b/api/functions_p.html @@ -174,7 +174,7 @@

                                - p -

                                  diff --git a/api/functions_q.html b/api/functions_q.html index 546aaffd..b135c9f2 100644 --- a/api/functions_q.html +++ b/api/functions_q.html @@ -93,7 +93,7 @@

                                  - q -

                                    diff --git a/api/functions_r.html b/api/functions_r.html index 2b7060e9..91357fd3 100644 --- a/api/functions_r.html +++ b/api/functions_r.html @@ -141,7 +141,7 @@

                                    - r -

                                      diff --git a/api/functions_s.html b/api/functions_s.html index 01c2dddb..61e017f8 100644 --- a/api/functions_s.html +++ b/api/functions_s.html @@ -211,7 +211,7 @@

                                      - s -

                                        diff --git a/api/functions_t.html b/api/functions_t.html index 00f2bf33..e513ee1c 100644 --- a/api/functions_t.html +++ b/api/functions_t.html @@ -123,7 +123,7 @@

                                        - t -

                                          diff --git a/api/functions_u.html b/api/functions_u.html index a535d451..fdc984ad 100644 --- a/api/functions_u.html +++ b/api/functions_u.html @@ -136,7 +136,7 @@

                                          - u -

                                            diff --git a/api/functions_v.html b/api/functions_v.html index 6a6f0767..e2d25dd4 100644 --- a/api/functions_v.html +++ b/api/functions_v.html @@ -96,7 +96,7 @@

                                            - v -

                                              diff --git a/api/functions_vars.html b/api/functions_vars.html index 79c54a8a..3b60a3e5 100644 --- a/api/functions_vars.html +++ b/api/functions_vars.html @@ -93,7 +93,7 @@

                                              - a -

                                                diff --git a/api/functions_vars_b.html b/api/functions_vars_b.html index bf5b641e..1818d5ea 100644 --- a/api/functions_vars_b.html +++ b/api/functions_vars_b.html @@ -102,7 +102,7 @@

                                                - b -

                                                  diff --git a/api/functions_vars_c.html b/api/functions_vars_c.html index 3edeeab1..8d1179a2 100644 --- a/api/functions_vars_c.html +++ b/api/functions_vars_c.html @@ -141,7 +141,7 @@

                                                  - c -

                                                    diff --git a/api/functions_vars_d.html b/api/functions_vars_d.html index 27c3fb9e..0df62384 100644 --- a/api/functions_vars_d.html +++ b/api/functions_vars_d.html @@ -117,7 +117,7 @@

                                                    - d -

                                                      diff --git a/api/functions_vars_e.html b/api/functions_vars_e.html index cfe55a64..4ab702fb 100644 --- a/api/functions_vars_e.html +++ b/api/functions_vars_e.html @@ -123,7 +123,7 @@

                                                      - e -

                                                        diff --git a/api/functions_vars_f.html b/api/functions_vars_f.html index ed2d36ca..934d94f9 100644 --- a/api/functions_vars_f.html +++ b/api/functions_vars_f.html @@ -94,7 +94,7 @@

                                                        - f -

                                                          diff --git a/api/functions_vars_g.html b/api/functions_vars_g.html index 151dc0fc..a33822a9 100644 --- a/api/functions_vars_g.html +++ b/api/functions_vars_g.html @@ -126,7 +126,7 @@

                                                          - g -

                                                            diff --git a/api/functions_vars_h.html b/api/functions_vars_h.html index 7815993d..4e377883 100644 --- a/api/functions_vars_h.html +++ b/api/functions_vars_h.html @@ -93,7 +93,7 @@

                                                            - h -

                                                              diff --git a/api/functions_vars_i.html b/api/functions_vars_i.html index d860873f..11cf564e 100644 --- a/api/functions_vars_i.html +++ b/api/functions_vars_i.html @@ -118,7 +118,7 @@

                                                              - i -

                                                                diff --git a/api/functions_vars_l.html b/api/functions_vars_l.html index d4889869..2e17ba71 100644 --- a/api/functions_vars_l.html +++ b/api/functions_vars_l.html @@ -111,7 +111,7 @@

                                                                - l -

                                                                  diff --git a/api/functions_vars_m.html b/api/functions_vars_m.html index b13c88e9..57390aaa 100644 --- a/api/functions_vars_m.html +++ b/api/functions_vars_m.html @@ -139,7 +139,7 @@

                                                                  - m -

                                                                    diff --git a/api/functions_vars_n.html b/api/functions_vars_n.html index 689ecdc1..f948b8c5 100644 --- a/api/functions_vars_n.html +++ b/api/functions_vars_n.html @@ -105,7 +105,7 @@

                                                                    - n -

                                                                      diff --git a/api/functions_vars_o.html b/api/functions_vars_o.html index 9d4d1422..e024be85 100644 --- a/api/functions_vars_o.html +++ b/api/functions_vars_o.html @@ -113,7 +113,7 @@

                                                                      - o -

                                                                        diff --git a/api/functions_vars_p.html b/api/functions_vars_p.html index 843f4d79..68315e75 100644 --- a/api/functions_vars_p.html +++ b/api/functions_vars_p.html @@ -174,7 +174,7 @@

                                                                        - p -

                                                                          diff --git a/api/functions_vars_q.html b/api/functions_vars_q.html index c44f52c1..5ca61b09 100644 --- a/api/functions_vars_q.html +++ b/api/functions_vars_q.html @@ -93,7 +93,7 @@

                                                                          - q -

                                                                            diff --git a/api/functions_vars_r.html b/api/functions_vars_r.html index 9dd426ee..28f99a84 100644 --- a/api/functions_vars_r.html +++ b/api/functions_vars_r.html @@ -141,7 +141,7 @@

                                                                            - r -

                                                                              diff --git a/api/functions_vars_s.html b/api/functions_vars_s.html index b27b16f0..c660aed2 100644 --- a/api/functions_vars_s.html +++ b/api/functions_vars_s.html @@ -211,7 +211,7 @@

                                                                              - s -

                                                                                diff --git a/api/functions_vars_t.html b/api/functions_vars_t.html index 89549167..22f4cdc4 100644 --- a/api/functions_vars_t.html +++ b/api/functions_vars_t.html @@ -123,7 +123,7 @@

                                                                                - t -

                                                                                  diff --git a/api/functions_vars_u.html b/api/functions_vars_u.html index 98848f96..cd91f720 100644 --- a/api/functions_vars_u.html +++ b/api/functions_vars_u.html @@ -136,7 +136,7 @@

                                                                                  - u -

                                                                                    diff --git a/api/functions_vars_v.html b/api/functions_vars_v.html index 3ec9e7ec..fce1cb27 100644 --- a/api/functions_vars_v.html +++ b/api/functions_vars_v.html @@ -96,7 +96,7 @@

                                                                                    - v -

                                                                                      diff --git a/api/fw__update_8h.html b/api/fw__update_8h.html index 502b5ac2..bdef2aaa 100644 --- a/api/fw__update_8h.html +++ b/api/fw__update_8h.html @@ -630,7 +630,7 @@

      diff --git a/api/fw__update_8h_source.html b/api/fw__update_8h_source.html index 6f414dc1..74b81129 100644 --- a/api/fw__update_8h_source.html +++ b/api/fw__update_8h_source.html @@ -272,7 +272,7 @@ diff --git a/api/globals.html b/api/globals.html index 186fd0b8..db3c88a9 100644 --- a/api/globals.html +++ b/api/globals.html @@ -1054,6 +1054,12 @@

      - a -

      • anjay_ret_double() : io.h
      • +
      • anjay_ret_float() +: io.h +
      • +
      • anjay_ret_i32() +: io.h +
      • anjay_ret_i64() : io.h
      • @@ -1072,6 +1078,9 @@

        - a -

        • anjay_ret_string() : io.h
        • +
        • anjay_ret_u32() +: io.h +
        • anjay_ret_u64() : io.h
        • @@ -1288,6 +1297,15 @@

          - a -

          • anjay_set_queue_mode_preference() : core.h
          • +
          • anjay_sim_bootstrap_perform() +: sim_bootstrap.h +
          • +
          • anjay_sim_bootstrap_perform_command_cb_t +: sim_bootstrap.h +
          • +
          • anjay_sim_bootstrap_stream_create() +: sim_bootstrap.h +
          • ANJAY_SMS_SECURITY_DTLS_PSK : dm.h
          • @@ -1498,6 +1516,9 @@

            - a -

            • ANJAY_WITH_MODULE_SERVER : anjay_config.h
            • +
            • ANJAY_WITH_MODULE_SIM_BOOTSTRAP +: anjay_config.h +
            • ANJAY_WITH_NET_STATS : anjay_config.h
            • @@ -1537,7 +1558,7 @@

              - a -

                diff --git a/api/globals_defs.html b/api/globals_defs.html index dd0e8f30..c707d34f 100644 --- a/api/globals_defs.html +++ b/api/globals_defs.html @@ -343,6 +343,9 @@

                - a -

                • ANJAY_WITH_MODULE_SERVER : anjay_config.h
                • +
                • ANJAY_WITH_MODULE_SIM_BOOTSTRAP +: anjay_config.h +
                • ANJAY_WITH_NET_STATS : anjay_config.h
                • @@ -382,7 +385,7 @@

                  - a -

                    diff --git a/api/globals_enum.html b/api/globals_enum.html index 2461f325..1473db5a 100644 --- a/api/globals_enum.html +++ b/api/globals_enum.html @@ -139,7 +139,7 @@ diff --git a/api/globals_eval.html b/api/globals_eval.html index 395ed987..0d9a35c6 100644 --- a/api/globals_eval.html +++ b/api/globals_eval.html @@ -363,7 +363,7 @@

                    - a -

                      diff --git a/api/globals_func.html b/api/globals_func.html index a9d1945e..dffcbc5c 100644 --- a/api/globals_func.html +++ b/api/globals_func.html @@ -411,6 +411,12 @@

                      - a -

                      • anjay_ret_double() : io.h
                      • +
                      • anjay_ret_float() +: io.h +
                      • +
                      • anjay_ret_i32() +: io.h +
                      • anjay_ret_i64() : io.h
                      • @@ -429,6 +435,9 @@

                        - a -

                        • anjay_ret_string() : io.h
                        • +
                        • anjay_ret_u32() +: io.h +
                        • anjay_ret_u64() : io.h
                        • @@ -570,6 +579,12 @@

                          - a -

                          • anjay_set_queue_mode_preference() : core.h
                          • +
                          • anjay_sim_bootstrap_perform() +: sim_bootstrap.h +
                          • +
                          • anjay_sim_bootstrap_stream_create() +: sim_bootstrap.h +
                          • anjay_smsdrv_cleanup() : sms.h
                          • @@ -618,7 +633,7 @@

                            - a -

                              diff --git a/api/globals_type.html b/api/globals_type.html index 6b9396a3..df487e41 100644 --- a/api/globals_type.html +++ b/api/globals_type.html @@ -333,6 +333,9 @@

                              - a -

    +
    +
    + +

    ◆ anjay_ret_float()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static int anjay_ret_float (anjay_output_ctx_tctx,
    float value 
    )
    +
    +inlinestatic
    +
    +

    Returns a 32-bit floating-point value from the data model handler.

    +
    Parameters
    + + + +
    ctxOutput context to operate on.
    valueThe value to return.
    +
    +
    +
    Returns
    0 on success, a negative value in case of error.
    + +
    +
    + +

    ◆ anjay_ret_i32()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static int anjay_ret_i32 (anjay_output_ctx_tctx,
    int32_t value 
    )
    +
    +inlinestatic
    +
    +

    Returns a 32-bit signed integer from the data model handler.

    +

    Note: the only difference between anjay_ret_i32 and anjay_ret_i64 is the size of the value parameter. Actual number of bytes sent on the wire depends on the value.

    +
    Parameters
    + + + +
    ctxOutput context to operate on.
    valueThe value to return.
    +
    +
    +
    Returns
    0 on success, a negative value in case of error.
    +
    @@ -1478,6 +1575,52 @@

    Returns
    0 on success, a negative value in case of error.