/*******************************************************************************
*                                 AMetal
*                       ----------------------------
*                       innovating embedded platform
*
* Copyright (c) 2001-2018 Guangzhou ZHIYUAN Electronics Co., Ltd.
* All rights reserved.
*
* Contact information:
* web site:    http://www.zlg.cn/
*******************************************************************************/

/**
 * \file
 * \brief 传感器 LPS22HB 驱动文件
 *
 * \internal
 * \par Modification history
 * - 1.00 18-12-03  yrz, first implementation.
 * \endinternal
 */

#include "am_sensor_lps22hb.h"
#include "am_gpio.h"
#include "am_vdebug.h"

/*******************************************************************************
 * 宏定义
 ******************************************************************************/
#define  __LPS22HB_REG_INTERRUPT_CFG    0X0B  /**< \brief 中断寄存器地址      */
#define  __LPS22HB_REG_THS_P_L          0X0C  /**< \brief 压力阈值低字节地址  */
#define  __LPS22HB_REG_THS_P_H          0X0D  /**< \brief 压力阈值高字节地址  */
#define  __LPS22HB_REG_WHO_AM_I         0X0F  /**< \brief ID寄存器地址        */
#define  __LPS22HB_REG_CTRL_REG1        0X10  /**< \brief 控制寄存器1地址     */
#define  __LPS22HB_REG_CTRL_REG2        0X11  /**< \brief 控制寄存器2地址     */
#define  __LPS22HB_REG_CTRL_REG3        0X12  /**< \brief 控制寄存器3地址     */
#define  __LPS22HB_REG_FIFO_CTRL        0X14  /**< \brief FIFO配置寄存器地址  */
#define  __LPS22HB_REG_REF_P_XL         0X15  /**< \brief 参考压力低字节地址  */
#define  __LPS22HB_REG_REF_P_L          0X16  /**< \brief 参考压力次低字节地址*/
#define  __LPS22HB_REG_REF_P_H          0X17  /**< \brief 参考压力高字节地址  */
#define  __LPS22HB_REG_RPDS_L           0X18  /**< \brief 压力补偿低字节地址  */
#define  __LPS22HB_REG_RPDS_H           0X19  /**< \brief 压力补偿高字节地址  */
#define  __LPS22HB_REG_RES_CONF         0X1A  /**< \brief 分辨率寄存器地址    */
#define  __LPS22HB_REG_INT_SOURCE       0X25  /**< \brief 中断源寄存器地址    */
#define  __LPS22HB_REG_FIFO_STATUS      0X26  /**< \brief FIFO状态寄存器地址  */
#define  __LPS22HB_REG_STATUS           0X27  /**< \brief 状态寄存器地址      */
#define  __LPS22HB_REG_PRESS_OUT_XL     0X28  /**< \brief 压力低字节地址      */
#define  __LPS22HB_REG_PRESS_OUT_L      0X29  /**< \brief 压力次低字节地址    */
#define  __LPS22HB_REG_PRESS_OUT_H      0X2A  /**< \brief 压力高字节地址      */
#define  __LPS22HB_REG_TEMP_OUT_L       0X2B  /**< \brief 温度低字节地址      */
#define  __LPS22HB_REG_TEMP_OUT_H       0X2C  /**< \brief 温度高字节地址      */
#define  __LPS22HB_REG_LPFP_RES         0X33  /**< \brief 滤波器复位地址      */

#define  __LPS22HB_CMD_RESET            0X04  /**< \brief 复位命令            */
#define  __LPS22HB_MY_ID                0XB1  /**< \brief ID值                */

#define  __LPS22HB_DIFF_EN       (0x01<<3)    /**< \brief 使能中断            */
#define  __LPS22HB_TRIGGER_READ  (0x01<<2)    /**< \brief 使能数据可读触发    */
#define  __LPS22HB_TRIGGER_CLOSE (~(0x01<<2)) /**< \brief 关闭数据可读触发    */

/** \brief 设置频率为一次 */
#define __LPS22HB_RATE_SET_ONE(data) (((data) & (~(0x7 << 4))) | (0x0 << 4))

/** \brief 设置频率为1 */
#define __LPS22HB_RATE_SET_1S(data)  (((data) & (~(0x7 << 4))) | (0x1 << 4))

/** \brief 设置频率为10 */
#define __LPS22HB_RATE_SET_10S(data) (((data) & (~(0x7 << 4))) | (0x2 << 4))

/** \brief 设置频率为25 */
#define __LPS22HB_RATE_SET_25S(data) (((data) & (~(0x7 << 4))) | (0x3 << 4))

/** \brief 设置频率为50 */
#define __LPS22HB_RATE_SET_50S(data) (((data) & (~(0x7 << 4))) | (0x4 << 4))

/** \brief 设置频率为75 */
#define __LPS22HB_RATE_SET_75S(data) (((data) & (~(0x7 << 4))) | (0x5 << 4))

/** \brief 开始一次数据采集 */
#define __LPS22HB_ONE_SHOT_START     (0x1)

/** \brief 获取PRESS状态位 */
#define __LPS22HB_GET_PRESS_STATUS(reg) (reg & 0x1)

/** \brief 获取TEM状态位 */
#define __LPS22HB_GET_TEM_STATUS(reg)   ((reg >> 1) & 0x1)

/** \brief 将两个int8转换为一个int16_t类型 */
#define __LPS22HB_UINT8_TO_UINT16(buff) \
                       (int16_t)(((uint16_t)(buff[1]) << 8) \
                               | ((uint16_t)(buff[0])))

/** \brief 将三个int8转换为一个int32_t类型 */
#define __LPS22HB_UINT8_TO_UINT32(buff) \
                       (uint32_t)(((uint32_t)(buff[2]) << 16) \
                               | ((uint32_t)(buff[1]) << 8)  \
                               | ((uint32_t)(buff[0])))

/** \brief 计算压强 */
#define __LPS22HB_GET_PRESS_VALUE(data)  ((double)(data) * 100 / 4096)

/** \brief 计算温度,并扩大10^6倍 */
#define __LPS22HB_GET_TEM_VALUE(data)    (data *1000000 / 100)

/*******************************************************************************
 * 本地函数声明
 ******************************************************************************/
/** \brief 获取该传感器某一通道的类型 */
am_local am_err_t __pfn_type_get (void *p_drv, int id);

/** \brief 获取传感器通道采样数据 */
am_local am_err_t __pfn_data_get (void            *p_drv,
                                  const int       *p_ids,
                                  int              num,
                                  am_sensor_val_t *p_buf);

/** \brief 使能传感器通道 */
am_local am_err_t __pfn_enable (void            *p_drv,
                                const int       *p_ids,
                                int              num,
                                am_sensor_val_t *p_result);

/** \brief 禁能传感器通道 */
am_local am_err_t __pfn_disable (void            *p_drv,
                                 const int       *p_ids,
                                 int              num,
                                 am_sensor_val_t *p_result);

/** \brief 配置传感器通道属性 */
am_local am_err_t __pfn_attr_set (void                  *p_drv,
                                  int                    id,
                                  int                    attr,
                                  const am_sensor_val_t *p_val);

/** \brief 获取传感器通道属性 */
am_local am_err_t __pfn_attr_get (void            *p_drv,
                                  int              id,
                                  int              attr,
                                  am_sensor_val_t *p_val);

/** \brief 设置触发，一个通道仅能设置一个触发回调函数 */
am_local am_err_t __pfn_trigger_cfg (void                   *p_drv,
                                     int                     id,
                                     uint32_t                flags,
                                     am_sensor_trigger_cb_t  pfn_cb,
                                     void                   *p_arg);

/** \brief 打开触发 */
am_local am_err_t __pfn_trigger_on (void *p_drv, int id);

/** \brief 关闭触发 */
am_local am_err_t __pfn_trigger_off (void *p_drv, int id);

/** \brief 传感器标准服务 */
am_local am_const struct am_sensor_drv_funcs __g_sensor_lps22hb_funcs = {
        __pfn_type_get,
        __pfn_data_get,
        __pfn_enable,
        __pfn_disable,
        __pfn_attr_set,
        __pfn_attr_get,
        __pfn_trigger_cfg,
        __pfn_trigger_on,
        __pfn_trigger_off
};
/*******************************************************************************
  Local functions
*******************************************************************************/
/**
 * \brief LPS22HB 写数据
 */
am_local am_err_t __lps22hb_write (am_sensor_lps22hb_dev_t *p_this,
                                  uint32_t                subaddr,
                                  uint8_t                *p_buf,
                                  uint32_t                nbytes)
{
    return am_i2c_write(&p_this->i2c_dev, subaddr, p_buf, nbytes);
}

/**
 * \brief LPS22HB 读数据
 */
am_local am_err_t __lps22hb_read (am_sensor_lps22hb_dev_t *p_this,
                                 uint32_t                subaddr,
                                 uint8_t                *p_buf,
                                 uint32_t                nbytes)
{
    return am_i2c_read(&p_this->i2c_dev, subaddr, p_buf, nbytes);
}

/** \breif 中断回调函数 */
am_local void __lps22hb_alarm_callback (void *p_arg)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_arg;

    am_isr_defer_job_add(&p_this->g_myjob);   /*< \brief 添加中断延迟处理任务 */
}

/** \breif 中断延迟函数 */
am_local void __am_pfnvoid_t (void *p_arg)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_arg;

    uint8_t reg_data[3];
    int32_t tem_data   = 0;

    /** \brief 获取压力值 */
    __lps22hb_read(p_this, __LPS22HB_REG_PRESS_OUT_XL, reg_data, 3);

    tem_data = __LPS22HB_UINT8_TO_UINT32(reg_data);

    /** \brief 压力 */
    p_this->data[0].val = __LPS22HB_GET_PRESS_VALUE(tem_data);
    p_this->data[0].unit = AM_SENSOR_UNIT_BASE; /*< \brief 单位默认为0:10^(0)*/

    /** \brief 获取温度 */
    __lps22hb_read(p_this, __LPS22HB_REG_TEMP_OUT_L, reg_data, 2);

    tem_data = __LPS22HB_UINT8_TO_UINT16(reg_data);

    /** \brief 温度 */
    p_this->data[1].val  = __LPS22HB_GET_TEM_VALUE(tem_data);
    p_this->data[1].unit = AM_SENSOR_UNIT_MICRO; /*< \brief 单位默认为-6:10^(-6)*/

    if (p_this->pfn_trigger_cb[0] &&
            (p_this->flags[0] & AM_SENSOR_TRIGGER_DATA_READY)) {
        p_this->pfn_trigger_cb[0](p_this->p_arg[0],
                                  AM_SENSOR_TRIGGER_DATA_READY);
    }

    if (p_this->pfn_trigger_cb[1] &&
            (p_this->flags[1] & AM_SENSOR_TRIGGER_DATA_READY)) {
        p_this->pfn_trigger_cb[1](p_this->p_arg[1],
                                  AM_SENSOR_TRIGGER_DATA_READY);
    }
}

/**
 * \brief 配置选择
 */
am_local am_err_t __reg_attr_set (am_sensor_lps22hb_dev_t *p_this, uint8_t rate)
{
     am_err_t ret = AM_OK;

     uint8_t rate_cfg = 0;

     ret = __lps22hb_read(p_this, __LPS22HB_REG_CTRL_REG1, &rate_cfg, 1);
     if (ret != AM_OK) {
         return ret;
     }

     switch (rate) {
     case 0:
         rate_cfg = __LPS22HB_RATE_SET_ONE(rate_cfg);
         break;
     case 1:
         rate_cfg = __LPS22HB_RATE_SET_1S(rate_cfg);
         break;
     case 2:
         rate_cfg = __LPS22HB_RATE_SET_10S(rate_cfg);
         break;
     case 3:
         rate_cfg = __LPS22HB_RATE_SET_25S(rate_cfg);
         break;
     case 4:
         rate_cfg = __LPS22HB_RATE_SET_50S(rate_cfg);
         break;
     case 5:
         rate_cfg = __LPS22HB_RATE_SET_75S(rate_cfg);
         break;
     default:
         break;
     }

     return __lps22hb_write(p_this, __LPS22HB_REG_CTRL_REG1, &rate_cfg, 1);
}

/** \brief 获取该传感器某一通道的类型 */
am_local am_err_t __pfn_type_get (void *p_drv, int id)
{
    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    if (id == AM_LPS22HB_CHAN_1) {
        return AM_SENSOR_TYPE_PRESS;
    } else if (id == AM_LPS22HB_CHAN_2) {
        return AM_SENSOR_TYPE_TEMPERATURE;
    } else {
        return -AM_ENODEV;
    }
}

/** \brief 获取传感器通道采样数据 */
am_local am_err_t __pfn_data_get (void            *p_drv,
                                  const int       *p_ids,
                                  int              num,
                                  am_sensor_val_t *p_buf)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    am_err_t ret        = AM_OK;
    uint8_t status_val  = 0;
    uint8_t reg_data[3] = {0};
    uint32_t tem_data   = 0;

    int cur_id = 0;
    int i = 0;

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    if (AM_BIT_GET(p_this->trigger, 7) != 1) {
        return -AM_EPERM;
    }

    for (i = 0; i < num; i++) {
        p_buf[i].unit = AM_SENSOR_UNIT_INVALID;
    }

    /* 若为1，则可能在数据准备就绪触发回调函数中使用 */
    if (num == 1) {
        cur_id = p_ids[0];
        /* 若打开数据准备就绪触发方式，则直接赋值 */
        if ((AM_BIT_GET(p_this->trigger, 2)) &&
                ((p_this->flags[0] & AM_SENSOR_TRIGGER_DATA_READY) ||
                 (p_this->flags[1] & AM_SENSOR_TRIGGER_DATA_READY))) {
            p_buf[0].val = p_this->data[cur_id].val;
            p_buf[0].unit= p_this->data[cur_id].unit;
            return AM_OK;
        }
    }

    /** \brief 获取可读状态值 */
    do {
        ret = __lps22hb_read(p_this, __LPS22HB_REG_STATUS, &status_val, 1);
        if (ret != AM_OK) {
            return ret;
        }
    } while (__LPS22HB_GET_PRESS_STATUS(status_val) != 0x1 ||
             __LPS22HB_GET_TEM_STATUS(status_val) != 0x1);

    for (i = 0; i < num; i++) {

        cur_id = p_ids[i];

        if (cur_id == 0) {

            /** \brief 获取压力值 */
            ret = __lps22hb_read(p_this, __LPS22HB_REG_PRESS_OUT_XL, reg_data, 3);
            if (ret != AM_OK) {
                return ret;
            }
            tem_data = __LPS22HB_UINT8_TO_UINT32(reg_data);

            /** \brief 压力 */
            p_buf[i].val = __LPS22HB_GET_PRESS_VALUE(tem_data);
            p_buf[i].unit = AM_SENSOR_UNIT_BASE; /*< \brief 单位默认为0:10^(0)*/

        } else if (cur_id == 1) {

            /** \brief 获取温度 */
            ret = __lps22hb_read(p_this, __LPS22HB_REG_TEMP_OUT_L, reg_data, 2);
            if (ret != AM_OK) {
                return ret;
            }
            tem_data = __LPS22HB_UINT8_TO_UINT16(reg_data);

            /** \brief 温度 */
            p_buf[i].val = __LPS22HB_GET_TEM_VALUE(tem_data);
            p_buf[i].unit = AM_SENSOR_UNIT_MICRO; /*< \brief 单位默认为-6:10^(-6)*/

        }else {
            return -AM_ENODEV;  /*< \brief 若此次通道不属于该传感器，直接返回 */
        }
    }

    ret = __lps22hb_read(p_this, __LPS22HB_REG_CTRL_REG2, &status_val, 1);
    if (ret != AM_OK) {
        return ret;
    }

    status_val |= __LPS22HB_ONE_SHOT_START;
    ret = __lps22hb_write(p_this, __LPS22HB_REG_CTRL_REG2, &status_val, 1);
    if (ret != AM_OK) {
        return ret;
    }
    return ret;
}

/** \brief 使能传感器通道 */
am_local am_err_t __pfn_enable (void            *p_drv,
                                const int       *p_ids,
                                int              num,
                                am_sensor_val_t *p_result)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    int i = 0;
    int cur_id = 0;

    uint8_t open_one_shot = 0;

    am_err_t ret = -AM_ENODEV;
    am_err_t curent_ret = AM_OK;

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    for (i = 0; i < num; i++) {

        cur_id = p_ids[i];

        if (cur_id == 0 || cur_id == 1) {
            if (p_result != NULL) {
                p_result[i].val = AM_OK;
            }
            ret = AM_OK;
        } else {
            if (p_result != NULL) {
                p_result[i].val = -AM_ENODEV;
            }
            curent_ret = -AM_ENODEV;
        }
    }

    if (ret != AM_OK) {    /**< \breif 如果本次没有该传感器的通道传入，则退出 */
        return curent_ret;
    }

    if (ret == AM_OK) {
        AM_BIT_SET(p_this->trigger, 7);
        AM_BIT_SET(p_this->trigger, 6);
        AM_BIT_SET(p_this->trigger, 5);
    }

    ret = __lps22hb_read(p_this, __LPS22HB_REG_CTRL_REG2, &open_one_shot, 1);
    if (ret != AM_OK) {
        curent_ret = ret;
    }

    open_one_shot |= __LPS22HB_ONE_SHOT_START;
    ret = __lps22hb_write(p_this, __LPS22HB_REG_CTRL_REG2, &open_one_shot, 1);
    if (ret != AM_OK) {
        curent_ret = ret;
    }

    return curent_ret;
}

/** \brief 禁能传感器通道 */
am_local am_err_t __pfn_disable (void            *p_drv,
                                 const int       *p_ids,
                                 int              num,
                                 am_sensor_val_t *p_result)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    int i = 0;
    int cur_id = 0;

    am_err_t ret = AM_OK;
    am_err_t cur_ret = AM_OK;

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    for (i = 0; i < num; i++) {

        cur_id = p_ids[i];

        if (cur_id == 0) {

            AM_BIT_CLR(p_this->trigger, 6);
            ret = AM_OK;

        } else if (cur_id == 1) {

            AM_BIT_CLR(p_this->trigger, 5);
            ret = AM_OK;

        } else {
            ret = -AM_ENODEV;
        }

        if (p_result != NULL) {
            p_result[i].val = ret;
        }
    }

    if ((AM_BIT_GET(p_this->trigger, 6) == 0)
            && (AM_BIT_GET(p_this->trigger, 5) == 0)) {

        if (cur_ret == AM_OK) {
            AM_BIT_CLR(p_this->trigger, 7);
        }
    }

    return cur_ret;
}

/** \brief 配置传感器通道属性 */
am_local am_err_t __pfn_attr_set (void                  *p_drv,
                                  int                    id,
                                  int                    attr,
                                  const am_sensor_val_t *p_val)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    am_err_t ret = AM_OK;
    am_err_t cur_ret = AM_OK;

    am_sensor_val_t sensor_val = {0, 0};

    am_sensor_val_t __sampling_rate = {0, 0};

    uint8_t rate = 0;

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    if ((id != AM_LPS22HB_CHAN_1) && (id != AM_LPS22HB_CHAN_2)) {
        return -AM_ENODEV;
    }

    switch (attr) {

    /* 修改频率 */
    case AM_SENSOR_ATTR_SAMPLING_RATE:

        if (p_val->val < 0 || p_val->val > 75) {
            return -AM_ENOTSUP;
        }

        sensor_val.unit = p_val->unit;
        sensor_val.val  = p_val->val;
        am_sensor_val_unit_convert(&sensor_val, 1, AM_SENSOR_UNIT_BASE);

        if (sensor_val.val == 0) {
            rate = 0;
            __sampling_rate.val = 0;
        } else if (sensor_val.val > 0 && sensor_val.val < 5) {
            rate = 1;
            __sampling_rate.val = 1;
        } else if (sensor_val.val >= 5 && sensor_val.val < 18) {
            rate = 2;
            __sampling_rate.val = 10;
        } else if (sensor_val.val >= 18 && sensor_val.val < 38) {
            rate = 3;
            __sampling_rate.val = 25;
        } else if (sensor_val.val >= 38 && sensor_val.val < 52) {
            rate = 4;
            __sampling_rate.val = 50;
        } else if (sensor_val.val >= 52 && sensor_val.val <= 75) {
            rate = 5;
            __sampling_rate.val = 75;
        }

        ret = __reg_attr_set(p_this, rate);
        if (ret != AM_OK) {
            cur_ret = ret;
        } else {
            p_this->sam_rate.val  = __sampling_rate.val;
            p_this->sam_rate.unit = __sampling_rate.unit;
        }

        break;

    default:
        cur_ret = -AM_ENOTSUP;
        break;
    }

    return cur_ret;
}

/** \brief 获取传感器通道属性 */
am_local am_err_t __pfn_attr_get (void            *p_drv,
                                  int              id,
                                  int              attr,
                                  am_sensor_val_t *p_val)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    am_err_t ret = AM_OK;

    if ((id != AM_LPS22HB_CHAN_1) && (id != AM_LPS22HB_CHAN_2)) {
        return -AM_ENODEV;
    }

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    switch (attr) {

    /* 获取采样频率 */
    case AM_SENSOR_ATTR_SAMPLING_RATE:

        p_val->val  = p_this->sam_rate.val;
        p_val->unit = p_this->sam_rate.unit;
        break;

    default:
        ret = -AM_ENOTSUP;
        break;

    }

    return ret;
}

/** \brief 设置触发，一个通道仅能设置一个触发回调函数 */
am_local am_err_t __pfn_trigger_cfg (void                   *p_drv,
                                     int                     id,
                                     uint32_t                flags,
                                     am_sensor_trigger_cb_t  pfn_cb,
                                     void                   *p_arg)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    if (id != 0 && id != 1) {
        return -AM_ENODEV;
    }

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    switch (flags) {
    case AM_SENSOR_TRIGGER_DATA_READY:
        break;
    default:
        return -AM_ENOTSUP;
    }

    p_this->pfn_trigger_cb[id] = pfn_cb;
    p_this->p_arg[id]          = p_arg;
    p_this->flags[id]          = flags;

    return AM_OK;
}

/** \brief 打开触发 */
am_local am_err_t __pfn_trigger_on (void *p_drv, int id)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    am_err_t ret = AM_OK;
    am_err_t cur_ret = AM_OK;

    uint8_t reg_data = 0;
    uint8_t reg_clear[5];

    if (id != 0 && id != 1) {
        return -AM_ENODEV;
    }

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    if (p_this->trigger & 0x4) {
        return AM_OK;
    }

    ret = __lps22hb_read(p_this, __LPS22HB_REG_INTERRUPT_CFG, &reg_data, 1);
    if ( ret != AM_OK) {
        cur_ret = ret;
    }

    /** \brief 使能中断生成 */
    reg_data |= __LPS22HB_DIFF_EN;
    ret = __lps22hb_write(p_this, __LPS22HB_REG_INTERRUPT_CFG, &reg_data, 1);
    if ( ret != AM_OK) {
        cur_ret = ret;
    }

    ret = __lps22hb_read(p_this, __LPS22HB_REG_CTRL_REG3, &reg_data, 1);
    if ( ret != AM_OK) {
        cur_ret = ret;
    }

    /** \brief 使能数据可读触发 */
    reg_data |= __LPS22HB_TRIGGER_READ;
    ret = __lps22hb_write(p_this, __LPS22HB_REG_CTRL_REG3, &reg_data, 1);
    if ( ret != AM_OK) {
        cur_ret = ret;
    }

    /* 配置触发引脚 */
    if (p_this->dev_info->trigger_pin != -1) {
        __lps22hb_read(p_this, __LPS22HB_REG_PRESS_OUT_XL, reg_clear, 5);
        ret = am_gpio_trigger_on(p_this->dev_info->trigger_pin);
        if (ret != AM_OK) {
            cur_ret = ret;
        }
    }

    if (cur_ret == AM_OK && id == 0) {
        AM_BIT_SET(p_this->trigger, 0);
        AM_BIT_SET(p_this->trigger, 2);
    }
    if (cur_ret == AM_OK && id == 1) {
        AM_BIT_SET(p_this->trigger, 1);
        AM_BIT_SET(p_this->trigger, 2);
    }

    return cur_ret;
}

/** \brief 关闭触发 */
am_local am_err_t __pfn_trigger_off (void *p_drv, int id)
{
    am_sensor_lps22hb_dev_t* p_this = (am_sensor_lps22hb_dev_t*)p_drv;

    am_err_t ret = AM_OK;
    am_err_t cur_ret = AM_OK;

    uint8_t reg_data = 0;

    am_sensor_val_t p_val = {0, 0};

    if (id != 0 && id != 1) {
        return -AM_ENODEV;
    }

    if (p_drv == NULL) {
        return -AM_EINVAL;
    }

    p_this->pfn_trigger_cb[id] = NULL;
    p_this->p_arg[id]          = NULL;
    p_this->flags[id]          = 0;

    AM_BIT_CLR(p_this->trigger, id);

    if ((p_this->trigger & 0x3) == 0) {

        ret = __lps22hb_read(p_this, __LPS22HB_REG_CTRL_REG3, &reg_data, 1);
        if (ret != AM_OK) {
            cur_ret = ret;
        }

        /** \brief 禁能数据可读触发 */
        reg_data &= __LPS22HB_TRIGGER_CLOSE;
        ret = __lps22hb_write(p_this, __LPS22HB_REG_CTRL_REG3, &reg_data, 1);
        if (ret != AM_OK) {
            cur_ret = ret;
        }

        ret = __pfn_attr_set(p_drv, id, AM_SENSOR_ATTR_SAMPLING_RATE, &p_val);
        if (ret != AM_OK) {
            cur_ret = ret;
        }

        if ((p_this->dev_info->trigger_pin != -1) && (cur_ret == AM_OK)) {
            ret = am_gpio_trigger_off(p_this->dev_info->trigger_pin);
            if (ret != AM_OK) {
                cur_ret = ret;
            }
        }

        if (cur_ret == AM_OK) {
            AM_BIT_CLR(p_this->trigger, 2);
        }
    }

    return cur_ret;
}

/*******************************************************************************
  Public functions
*******************************************************************************/
/**
 * \brief 传感器 LPS22HB 初始化
 */
am_sensor_handle_t am_sensor_lps22hb_init (
        am_sensor_lps22hb_dev_t           *p_dev,
        const am_sensor_lps22hb_devinfo_t *p_devinfo,
        am_i2c_handle_t                   handle)
{
    am_err_t ret = AM_OK;
    am_err_t cur_ret = AM_OK;

    uint8_t ctrl_reg2  = 0;
    uint8_t lps22hb_id = 0;

    if (p_dev == NULL || p_devinfo == NULL) {
        return NULL;
    }

    am_i2c_mkdev(&p_dev->i2c_dev,
                 handle,
                 p_devinfo->i2c_addr,
                 AM_I2C_ADDR_7BIT | AM_I2C_SUBADDR_1BYTE);

    p_dev->lps22hb_dev.p_drv   = p_dev;
    p_dev->lps22hb_dev.p_funcs = &__g_sensor_lps22hb_funcs;
    p_dev->dev_info            = p_devinfo;
    p_dev->pfn_trigger_cb[0]   = NULL;
    p_dev->pfn_trigger_cb[1]   = NULL;
    p_dev->p_arg[0]            = NULL;
    p_dev->p_arg[1]            = NULL;
    p_dev->flags[0]            = 0;
    p_dev->flags[1]            = 0;
    p_dev->sam_rate.val        = 0;
    p_dev->sam_rate.unit       = 0;
    p_dev->trigger             = 0;
    p_dev->data[0].val         = 0;
    p_dev->data[0].unit        = AM_SENSOR_UNIT_INVALID;
    p_dev->data[1].val         = 0;
    p_dev->data[1].unit        = AM_SENSOR_UNIT_INVALID;

    /* 外部中断连接 */
    if (p_devinfo->trigger_pin != -1) {
        am_gpio_pin_cfg(p_devinfo->trigger_pin, AM_GPIO_INPUT);
        am_gpio_trigger_connect(p_devinfo->trigger_pin,
                                __lps22hb_alarm_callback,
                                (void*)p_dev);
        am_gpio_trigger_cfg(p_devinfo->trigger_pin, AM_GPIO_TRIGGER_RISE);
    }

    am_isr_defer_job_init(&p_dev->g_myjob, __am_pfnvoid_t, p_dev, 1);

    ret = __lps22hb_read(p_dev, __LPS22HB_REG_CTRL_REG2, &ctrl_reg2, 1);
    if (ret != AM_OK) {
        cur_ret = ret;
    }
    /* 复位LPS22HB */
    ctrl_reg2 |= __LPS22HB_CMD_RESET;
    ret = __lps22hb_write(p_dev, __LPS22HB_REG_CTRL_REG2, &ctrl_reg2, 1);
    if (ret != AM_OK) {
        cur_ret = ret;
    }

    /* 读取ID */
    ret = __lps22hb_read(p_dev, __LPS22HB_REG_WHO_AM_I, &lps22hb_id, 1);
    if (ret != AM_OK || lps22hb_id != __LPS22HB_MY_ID) {
        cur_ret = ret;
    }

    if (cur_ret != AM_OK) {
        am_kprintf("\r\nSensor LPS22HB Init is ERROR! \r\n");
        return NULL;
    }

    return &(p_dev->lps22hb_dev);
}

/**
 * \brief 传感器 LPS22HB 去初始化
 */
am_err_t am_sensor_lps22hb_deinit (am_sensor_handle_t handle)
{
    am_sensor_lps22hb_dev_t *p_dev = handle->p_drv;

    if (handle == NULL) {
        return -AM_EINVAL;
    }

    p_dev->lps22hb_dev.p_drv   = NULL;
    p_dev->lps22hb_dev.p_funcs = NULL;
    p_dev->dev_info            = NULL;

    return AM_OK;
}

/* end of file */


# Copyright 1999-2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

EAPI=5

# ebuild generated by hackport 0.4.6.9999

CABAL_FEATURES="lib profile haddock hoogle hscolour test-suite"
inherit haskell-cabal

DESCRIPTION="Components of paths"
HOMEPAGE="https://hackage.haskell.org/package/path-pieces"
SRC_URI="https://hackage.haskell.org/package/${P}/${P}.tar.gz"

LICENSE="BSD"
SLOT="0/${PV}"
KEYWORDS="~amd64 ~x86"
IUSE=""

RDEPEND=">=dev-haskell/text-0.5:=[profile?]
	>=dev-lang/ghc-7.4.1:=
"
DEPEND="${RDEPEND}
	>=dev-haskell/cabal-1.8
	test? ( >=dev-haskell/hspec-1.3
		dev-haskell/hunit
		dev-haskell/quickcheck )
"


package glue

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/awslabs/goformation/v4/cloudformation/policies"
)

// Crawler AWS CloudFormation Resource (AWS::Glue::Crawler)
// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html
type Crawler struct {

	// Classifiers AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-classifiers
	Classifiers []string `json:"Classifiers,omitempty"`

	// Configuration AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-configuration
	Configuration string `json:"Configuration,omitempty"`

	// CrawlerSecurityConfiguration AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-crawlersecurityconfiguration
	CrawlerSecurityConfiguration string `json:"CrawlerSecurityConfiguration,omitempty"`

	// DatabaseName AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-databasename
	DatabaseName string `json:"DatabaseName,omitempty"`

	// Description AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-description
	Description string `json:"Description,omitempty"`

	// Name AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-name
	Name string `json:"Name,omitempty"`

	// Role AWS CloudFormation Property
	// Required: true
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-role
	Role string `json:"Role,omitempty"`

	// Schedule AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-schedule
	Schedule *Crawler_Schedule `json:"Schedule,omitempty"`

	// SchemaChangePolicy AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-schemachangepolicy
	SchemaChangePolicy *Crawler_SchemaChangePolicy `json:"SchemaChangePolicy,omitempty"`

	// TablePrefix AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-tableprefix
	TablePrefix string `json:"TablePrefix,omitempty"`

	// Tags AWS CloudFormation Property
	// Required: false
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-tags
	Tags interface{} `json:"Tags,omitempty"`

	// Targets AWS CloudFormation Property
	// Required: true
	// See: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-targets
	Targets *Crawler_Targets `json:"Targets,omitempty"`

	// AWSCloudFormationDeletionPolicy represents a CloudFormation DeletionPolicy
	AWSCloudFormationDeletionPolicy policies.DeletionPolicy `json:"-"`

	// AWSCloudFormationUpdateReplacePolicy represents a CloudFormation UpdateReplacePolicy
	AWSCloudFormationUpdateReplacePolicy policies.UpdateReplacePolicy `json:"-"`

	// AWSCloudFormationDependsOn stores the logical ID of the resources to be created before this resource
	AWSCloudFormationDependsOn []string `json:"-"`

	// AWSCloudFormationMetadata stores structured data associated with this resource
	AWSCloudFormationMetadata map[string]interface{} `json:"-"`

	// AWSCloudFormationCondition stores the logical ID of the condition that must be satisfied for this resource to be created
	AWSCloudFormationCondition string `json:"-"`
}

// AWSCloudFormationType returns the AWS CloudFormation resource type
func (r *Crawler) AWSCloudFormationType() string {
	return "AWS::Glue::Crawler"
}

// MarshalJSON is a custom JSON marshalling hook that embeds this object into
// an AWS CloudFormation JSON resource's 'Properties' field and adds a 'Type'.
func (r Crawler) MarshalJSON() ([]byte, error) {
	type Properties Crawler
	return json.Marshal(&struct {
		Type                string
		Properties          Properties
		DependsOn           []string                     `json:"DependsOn,omitempty"`
		Metadata            map[string]interface{}       `json:"Metadata,omitempty"`
		DeletionPolicy      policies.DeletionPolicy      `json:"DeletionPolicy,omitempty"`
		UpdateReplacePolicy policies.UpdateReplacePolicy `json:"UpdateReplacePolicy,omitempty"`
		Condition           string                       `json:"Condition,omitempty"`
	}{
		Type:                r.AWSCloudFormationType(),
		Properties:          (Properties)(r),
		DependsOn:           r.AWSCloudFormationDependsOn,
		Metadata:            r.AWSCloudFormationMetadata,
		DeletionPolicy:      r.AWSCloudFormationDeletionPolicy,
		UpdateReplacePolicy: r.AWSCloudFormationUpdateReplacePolicy,
		Condition:           r.AWSCloudFormationCondition,
	})
}

// UnmarshalJSON is a custom JSON unmarshalling hook that strips the outer
// AWS CloudFormation resource object, and just keeps the 'Properties' field.
func (r *Crawler) UnmarshalJSON(b []byte) error {
	type Properties Crawler
	res := &struct {
		Type                string
		Properties          *Properties
		DependsOn           []string
		Metadata            map[string]interface{}
		DeletionPolicy      string
		UpdateReplacePolicy string
		Condition           string
	}{}

	dec := json.NewDecoder(bytes.NewReader(b))
	dec.DisallowUnknownFields() // Force error if unknown field is found

	if err := dec.Decode(&res); err != nil {
		fmt.Printf("ERROR: %s\n", err)
		return err
	}

	// If the resource has no Properties set, it could be nil
	if res.Properties != nil {
		*r = Crawler(*res.Properties)
	}
	if res.DependsOn != nil {
		r.AWSCloudFormationDependsOn = res.DependsOn
	}
	if res.Metadata != nil {
		r.AWSCloudFormationMetadata = res.Metadata
	}
	if res.DeletionPolicy != "" {
		r.AWSCloudFormationDeletionPolicy = policies.DeletionPolicy(res.DeletionPolicy)
	}
	if res.UpdateReplacePolicy != "" {
		r.AWSCloudFormationUpdateReplacePolicy = policies.UpdateReplacePolicy(res.UpdateReplacePolicy)
	}
	if res.Condition != "" {
		r.AWSCloudFormationCondition = res.Condition
	}
	return nil
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Identifier</key>
	<string>org.cocos3d.cocos3dStatLibProjOSX</string>
	<key>Name</key>
	<string>Cocos3D Static Library</string>
	<key>Description</key>
	<string>A Cocos3D static library that can be added to any existing Cocos2D or SpriteBuilder project for a Mac running OSX.</string>
	<key>Kind</key>
	<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
	<key>Concrete</key>
	<true/>
	<key>SortOrder</key>
	<integer>0</integer>
	<key>Ancestors</key>
	<array>
		<string>org.cocos3d.cocos3dProjOSX</string>
		<string>org.cocos3d.cocos3dStatLibBase</string>
	</array>
</dict>
</plist>


MatrixXd X = MatrixXd::Random(5,5);
MatrixXd A = X * X.transpose();
X = MatrixXd::Random(5,5);
MatrixXd B = X * X.transpose();

GeneralizedSelfAdjointEigenSolver<MatrixXd> es(A,B,EigenvaluesOnly);
cout << "The eigenvalues of the pencil (A,B) are:" << endl << es.eigenvalues() << endl;
es.compute(B,A,false);
cout << "The eigenvalues of the pencil (B,A) are:" << endl << es.eigenvalues() << endl;


/*
          Application : Functions
          Author      : Mahmoud Kibreet
          Date        : 2018.01.27
*/

# Functions - Scope - Pass by Value - Pass by reference

? "Hello , world"
x = 10 # global

def main
? "Welcome to the main function"
one()

def one
	? x


// RUN: %clang_cc1 -O0 -cl-std=CL2.0 -fsyntax-only -verify  %s

global atomic_int a1 = 0;

kernel void test_atomic_initialization() {
  a1 = 1; // expected-error {{atomic variable can be assigned to a variable only in global address space}}
  atomic_int a2 = 0; // expected-error {{atomic variable can be initialized to a variable only in global address space}}
  private atomic_int a3 = 0; // expected-error {{atomic variable can be initialized to a variable only in global address space}}
  local atomic_int a4 = 0; // expected-error {{'__local' variable cannot have an initializer}}
  global atomic_int a5 = 0; // expected-error {{function scope variable cannot be declared in global address space}}
  static global atomic_int a6 = 0;
}


///Name & Description
event_inherited();

relicName = loc_key("RELIC_MEGAQUANTUMCHIP_NAME");
relicDescription = loc_key("RELIC_MEGAQUANTUMCHIP_LONG");


from digitalio import DigitalInOut, Pull, Direction
import board

button_a = DigitalInOut(board.BUTTON_A)
button_a.direction = Direction.INPUT
button_a.pull = Pull.DOWN

button_b = DigitalInOut(board.BUTTON_B)
button_b.direction = Direction.INPUT
button_b.pull = Pull.DOWN

switch = DigitalInOut(board.SLIDE_SWITCH)
switch.direction = Direction.INPUT
switch.pull = Pull.UP

# Get the values of the switches now
last_buttona = button_a.value
last_buttonb = button_b.value
last_switch = switch.value


#!/bin/bash

#
# builds a production meteor bundle directory
#
set -e

# Fix permissions warning in Meteor >=1.4.2.1 without breaking
# earlier versions of Meteor with --unsafe-perm or --allow-superuser
# https://github.com/meteor/meteor/issues/7959
export METEOR_ALLOW_SUPERUSER=true

cd $APP_SOURCE_DIR

# Install app deps
printf "\n[-] Running npm install in app directory...\n\n"
meteor npm install

# build the bundle
printf "\n[-] Building Meteor application...\n\n"
mkdir -p $APP_BUNDLE_DIR
meteor build --directory $APP_BUNDLE_DIR

# run npm install in bundle
printf "\n[-] Running npm install in the server bundle...\n\n"
cd $APP_BUNDLE_DIR/bundle/programs/server/
meteor npm install --production

# put the entrypoint script in WORKDIR
mv $BUILD_SCRIPTS_DIR/entrypoint.sh $APP_BUNDLE_DIR/bundle/entrypoint.sh

# change ownership of the app to the node user
chown -R node:node $APP_BUNDLE_DIR


14
0 0 1 0 0 0 0 0 0 0 0 1 1 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 1 1
0 1 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 1 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 1 0 0 0 0 0 0 1 0 1
0 0 1 0 0 0 0 0 1 0 0 0 1 0
1 0 0 0 0 0 0 0 0 1 0 0 0 0
1 0 0 1 0 1 0 0 0 0 1 0 0 0
0 0 0 1 0 0 0 0 0 1 0 0 0 0


# AUTOGENERATED FILE
FROM balenalib/kitra520-debian:buster-build

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        ca-certificates \
        curl \
        \
# .NET Core dependencies
        libc6 \
        libgcc1 \
        libgssapi-krb5-2 \
        libicu63 \
        libssl1.1 \
        libstdc++6 \
        zlib1g \
    && rm -rf /var/lib/apt/lists/*

# Configure web servers to bind to port 80 when present
ENV ASPNETCORE_URLS=http://+:80 \
    # Enable detection of running in a container
    DOTNET_RUNNING_IN_CONTAINER=true

# Install .NET Core
ENV DOTNET_VERSION 3.1.5

RUN curl -SL --output dotnet.tar.gz "https://dotnetcli.blob.core.windows.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-linux-arm.tar.gz" \
    && dotnet_sha512='c3eb0453893ca7f758e491780031ef0b1323a14c080740847aea652b5c4db99d30e8b3b27fcd306c3a098dc838572c41d3ea871156ba62d9302df32e63a28835' \
    && echo "$dotnet_sha512 dotnet.tar.gz" | sha512sum -c - \
    && mkdir -p /usr/share/dotnet \
    && tar -zxf dotnet.tar.gz -C /usr/share/dotnet \
    && rm dotnet.tar.gz \
    && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

ENV ASPNETCORE_VERSION 3.1.5

RUN curl -SL --output aspnetcore.tar.gz "https://dotnetcli.blob.core.windows.net/dotnet/aspnetcore/Runtime/$ASPNETCORE_VERSION/aspnetcore-runtime-$ASPNETCORE_VERSION-linux-arm.tar.gz" \
    && aspnetcore_sha512='090ef0da2454935015184f4b4b968c049eb806d19dcd258af06265e922f9d0df209fa9f84ebefe9e7cb6bb391dd0459eb3b5f7e8609fca2cffd75d3801c1b6a7' \
    && echo "$aspnetcore_sha512  aspnetcore.tar.gz" | sha512sum -c - \
    && mkdir -p /usr/share/dotnet \
    && tar -zxf aspnetcore.tar.gz -C /usr/share/dotnet ./shared/Microsoft.AspNetCore.App \
    && rm aspnetcore.tar.gz

CMD ["echo","'No CMD command was set in Dockerfile! Details about CMD command could be found in Dockerfile Guide section in our Docs. Here's the link: https://balena.io/docs"]

 RUN curl -SLO "https://raw.githubusercontent.com/balena-io-library/base-images/8accad6af708fca7271c5c65f18a86782e19f877/scripts/assets/tests/test-stack@dotnet.sh" \
  && echo "Running test-stack@dotnet" \
  && chmod +x test-stack@dotnet.sh \
  && bash test-stack@dotnet.sh \
  && rm -rf test-stack@dotnet.sh

RUN [ ! -d /.balena/messages ] && mkdir -p /.balena/messages; echo 'Here are a few details about this Docker image (For more information please visit https://www.balena.io/docs/reference/base-images/base-images/): \nArchitecture: ARM v7 \nOS: Debian Buster \nVariant: build variant \nDefault variable(s): UDEV=off \nThe following software stack is preinstalled: \ndotnet 3.1-aspnet \nExtra features: \n- Easy way to install packages with `install_packages <package-name>` command \n- Run anywhere with cross-build feature  (for ARM only) \n- Keep the container idling with `balena-idle` command \n- Show base image details with `balena-info` command' > /.balena/messages/image-info

RUN echo '#!/bin/sh.real\nbalena-info\nrm -f /bin/sh\ncp /bin/sh.real /bin/sh\n/bin/sh "$@"' > /bin/sh-shim \
	&& chmod +x /bin/sh-shim \
	&& cp /bin/sh /bin/sh.real \
	&& mv /bin/sh-shim /bin/sh


using System;

namespace MonoTorrent.Client
{
    public class StatsUpdateEventArgs : EventArgs
    {
        public StatsUpdateEventArgs ()
        {
        }
    }
}


drop index rhn_cf_member_uq;


// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
//
// Most common scenarios will want to use autocert subdirectory instead,
// which provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package acme

import (
	"bytes"
	"context"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"

const (
	maxChainLen = 5       // max depth and breadth of a certificate chain
	maxCertSize = 1 << 20 // max size of a certificate, in bytes

	// Max number of collected nonces kept in memory.
	// Expect usual peak of 1 or 2.
	maxNonces = 100
)

// CertOption is an optional argument type for Client methods which manipulate
// certificate data.
type CertOption interface {
	privateCertOpt()
}

// WithKey creates an option holding a private/public key pair.
// The private part signs a certificate, and the public part represents the signee.
func WithKey(key crypto.Signer) CertOption {
	return &certOptKey{key}
}

type certOptKey struct {
	key crypto.Signer
}

func (*certOptKey) privateCertOpt() {}

// WithTemplate creates an option for specifying a certificate template.
// See x509.CreateCertificate for template usage details.
//
// In TLSSNIxChallengeCert methods, the template is also used as parent,
// resulting in a self-signed certificate.
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
func WithTemplate(t *x509.Certificate) CertOption {
	return (*certOptTemplate)(t)
}

type certOptTemplate x509.Certificate

func (*certOptTemplate) privateCertOpt() {}

// Client is an ACME client.
// The only required field is Key. An example of creating a client with a new key
// is as follows:
//
// 	key, err := rsa.GenerateKey(rand.Reader, 2048)
// 	if err != nil {
// 		log.Fatal(err)
// 	}
// 	client := &Client{Key: key}
//
type Client struct {
	// Key is the account key used to register with a CA and sign requests.
	// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
	Key crypto.Signer

	// HTTPClient optionally specifies an HTTP client to use
	// instead of http.DefaultClient.
	HTTPClient *http.Client

	// DirectoryURL points to the CA directory endpoint.
	// If empty, LetsEncryptURL is used.
	// Mutating this value after a successful call of Client's Discover method
	// will have no effect.
	DirectoryURL string

	dirMu sync.Mutex // guards writes to dir
	dir   *Directory // cached result of Client's Discover method

	noncesMu sync.Mutex
	nonces   map[string]struct{} // nonces collected from previous responses
}

// Discover performs ACME server discovery using c.DirectoryURL.
//
// It caches successful result. So, subsequent calls will not result in
// a network round-trip. This also means mutating c.DirectoryURL after successful call
// of this method will have no effect.
func (c *Client) Discover(ctx context.Context) (Directory, error) {
	c.dirMu.Lock()
	defer c.dirMu.Unlock()
	if c.dir != nil {
		return *c.dir, nil
	}

	dirURL := c.DirectoryURL
	if dirURL == "" {
		dirURL = LetsEncryptURL
	}
	res, err := c.get(ctx, dirURL)
	if err != nil {
		return Directory{}, err
	}
	defer res.Body.Close()
	c.addNonce(res.Header)
	if res.StatusCode != http.StatusOK {
		return Directory{}, responseError(res)
	}

	var v struct {
		Reg    string `json:"new-reg"`
		Authz  string `json:"new-authz"`
		Cert   string `json:"new-cert"`
		Revoke string `json:"revoke-cert"`
		Meta   struct {
			Terms   string   `json:"terms-of-service"`
			Website string   `json:"website"`
			CAA     []string `json:"caa-identities"`
		}
	}
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return Directory{}, err
	}
	c.dir = &Directory{
		RegURL:    v.Reg,
		AuthzURL:  v.Authz,
		CertURL:   v.Cert,
		RevokeURL: v.Revoke,
		Terms:     v.Meta.Terms,
		Website:   v.Meta.Website,
		CAA:       v.Meta.CAA,
	}
	return *c.dir, nil
}

// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such scenario the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
	if _, err := c.Discover(ctx); err != nil {
		return nil, "", err
	}

	req := struct {
		Resource  string `json:"resource"`
		CSR       string `json:"csr"`
		NotBefore string `json:"notBefore,omitempty"`
		NotAfter  string `json:"notAfter,omitempty"`
	}{
		Resource: "new-cert",
		CSR:      base64.RawURLEncoding.EncodeToString(csr),
	}
	now := timeNow()
	req.NotBefore = now.Format(time.RFC3339)
	if exp > 0 {
		req.NotAfter = now.Add(exp).Format(time.RFC3339)
	}

	res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req)
	if err != nil {
		return nil, "", err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusCreated {
		return nil, "", responseError(res)
	}

	curl := res.Header.Get("location") // cert permanent URL
	if res.ContentLength == 0 {
		// no cert in the body; poll until we get it
		cert, err := c.FetchCert(ctx, curl, bundle)
		return cert, curl, err
	}
	// slurp issued cert and CA chain, if requested
	cert, err := c.responseCert(ctx, res, bundle)
	return cert, curl, err
}

// FetchCert retrieves already issued certificate from the given url, in DER format.
// It retries the request until the certificate is successfully retrieved,
// context is cancelled by the caller or an error response is received.
//
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
//
// FetchCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
	for {
		res, err := c.get(ctx, url)
		if err != nil {
			return nil, err
		}
		defer res.Body.Close()
		if res.StatusCode == http.StatusOK {
			return c.responseCert(ctx, res, bundle)
		}
		if res.StatusCode > 299 {
			return nil, responseError(res)
		}
		d := retryAfter(res.Header.Get("retry-after"), 3*time.Second)
		select {
		case <-time.After(d):
			// retry
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

// RevokeCert revokes a previously issued certificate cert, provided in DER format.
//
// The key argument, used to sign the request, must be authorized
// to revoke the certificate. It's up to the CA to decide which keys are authorized.
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
	if _, err := c.Discover(ctx); err != nil {
		return err
	}

	body := &struct {
		Resource string `json:"resource"`
		Cert     string `json:"certificate"`
		Reason   int    `json:"reason"`
	}{
		Resource: "revoke-cert",
		Cert:     base64.RawURLEncoding.EncodeToString(cert),
		Reason:   int(reason),
	}
	if key == nil {
		key = c.Key
	}
	res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return responseError(res)
	}
	return nil
}

// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
// during account registration. See Register method of Client for more details.
func AcceptTOS(tosURL string) bool { return true }

// Register creates a new account registration by following the "new-reg" flow.
// It returns registered account. The a argument is not modified.
//
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL string) bool) (*Account, error) {
	if _, err := c.Discover(ctx); err != nil {
		return nil, err
	}

	var err error
	if a, err = c.doReg(ctx, c.dir.RegURL, "new-reg", a); err != nil {
		return nil, err
	}
	var accept bool
	if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms {
		accept = prompt(a.CurrentTerms)
	}
	if accept {
		a.AgreedTerms = a.CurrentTerms
		a, err = c.UpdateReg(ctx, a)
	}
	return a, err
}

// GetReg retrieves an existing registration.
// The url argument is an Account URI.
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
	a, err := c.doReg(ctx, url, "reg", nil)
	if err != nil {
		return nil, err
	}
	a.URI = url
	return a, nil
}

// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
	uri := a.URI
	a, err := c.doReg(ctx, uri, "reg", a)
	if err != nil {
		return nil, err
	}
	a.URI = uri
	return a, nil
}

// Authorize performs the initial step in an authorization flow.
// The caller will then need to choose from and perform a set of returned
// challenges using c.Accept in order to successfully complete authorization.
//
// If an authorization has been previously granted, the CA may return
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
// need not fulfill any challenge and can proceed to requesting a certificate.
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
	if _, err := c.Discover(ctx); err != nil {
		return nil, err
	}

	type authzID struct {
		Type  string `json:"type"`
		Value string `json:"value"`
	}
	req := struct {
		Resource   string  `json:"resource"`
		Identifier authzID `json:"identifier"`
	}{
		Resource:   "new-authz",
		Identifier: authzID{Type: "dns", Value: domain},
	}
	res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusCreated {
		return nil, responseError(res)
	}

	var v wireAuthz
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, fmt.Errorf("acme: invalid response: %v", err)
	}
	if v.Status != StatusPending && v.Status != StatusValid {
		return nil, fmt.Errorf("acme: unexpected status: %s", v.Status)
	}
	return v.authorization(res.Header.Get("Location")), nil
}

// GetAuthorization retrieves an authorization identified by the given URL.
//
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
	res, err := c.get(ctx, url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
		return nil, responseError(res)
	}
	var v wireAuthz
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, fmt.Errorf("acme: invalid response: %v", err)
	}
	return v.authorization(url), nil
}

// RevokeAuthorization relinquishes an existing authorization identified
// by the given URL.
// The url argument is an Authorization.URI value.
//
// If successful, the caller will be required to obtain a new authorization
// using the Authorize method before being able to request a new certificate
// for the domain associated with the authorization.
//
// It does not revoke existing certificates.
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
	req := struct {
		Resource string `json:"resource"`
		Status   string `json:"status"`
		Delete   bool   `json:"delete"`
	}{
		Resource: "authz",
		Status:   "deactivated",
		Delete:   true,
	}
	res, err := c.retryPostJWS(ctx, c.Key, url, req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return responseError(res)
	}
	return nil
}

// WaitAuthorization polls an authorization at the given URL
// until it is in one of the final states, StatusValid or StatusInvalid,
// or the context is done.
//
// It returns a non-nil Authorization only if its Status is StatusValid.
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
	sleep := sleeper(ctx)
	for {
		res, err := c.get(ctx, url)
		if err != nil {
			return nil, err
		}
		retry := res.Header.Get("retry-after")
		if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
			res.Body.Close()
			if err := sleep(retry, 1); err != nil {
				return nil, err
			}
			continue
		}
		var raw wireAuthz
		err = json.NewDecoder(res.Body).Decode(&raw)
		res.Body.Close()
		if err != nil {
			if err := sleep(retry, 0); err != nil {
				return nil, err
			}
			continue
		}
		if raw.Status == StatusValid {
			return raw.authorization(url), nil
		}
		if raw.Status == StatusInvalid {
			return nil, raw.error(url)
		}
		if err := sleep(retry, 0); err != nil {
			return nil, err
		}
	}
}

// GetChallenge retrieves the current status of an challenge.
//
// A client typically polls a challenge status using this method.
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
	res, err := c.get(ctx, url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
		return nil, responseError(res)
	}
	v := wireChallenge{URI: url}
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, fmt.Errorf("acme: invalid response: %v", err)
	}
	return v.challenge(), nil
}

// Accept informs the server that the client accepts one of its challenges
// previously obtained with c.Authorize.
//
// The server will then perform the validation asynchronously.
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
	auth, err := keyAuth(c.Key.Public(), chal.Token)
	if err != nil {
		return nil, err
	}

	req := struct {
		Resource string `json:"resource"`
		Type     string `json:"type"`
		Auth     string `json:"keyAuthorization"`
	}{
		Resource: "challenge",
		Type:     chal.Type,
		Auth:     auth,
	}
	res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	// Note: the protocol specifies 200 as the expected response code, but
	// letsencrypt seems to be returning 202.
	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
		return nil, responseError(res)
	}

	var v wireChallenge
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, fmt.Errorf("acme: invalid response: %v", err)
	}
	return v.challenge(), nil
}

// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
// A TXT record containing the returned value must be provisioned under
// "_acme-challenge" name of the domain being validated.
//
// The token argument is a Challenge.Token value.
func (c *Client) DNS01ChallengeRecord(token string) (string, error) {
	ka, err := keyAuth(c.Key.Public(), token)
	if err != nil {
		return "", err
	}
	b := sha256.Sum256([]byte(ka))
	return base64.RawURLEncoding.EncodeToString(b[:]), nil
}

// HTTP01ChallengeResponse returns the response for an http-01 challenge.
// Servers should respond with the value to HTTP requests at the URL path
// provided by HTTP01ChallengePath to validate the challenge and prove control
// over a domain name.
//
// The token argument is a Challenge.Token value.
func (c *Client) HTTP01ChallengeResponse(token string) (string, error) {
	return keyAuth(c.Key.Public(), token)
}

// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
// should be provided by the servers.
// The response value can be obtained with HTTP01ChallengeResponse.
//
// The token argument is a Challenge.Token value.
func (c *Client) HTTP01ChallengePath(token string) string {
	return "/.well-known/acme-challenge/" + token
}

// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name.
//
// The implementation is incomplete in that the returned value is a single certificate,
// computed only for Z0 of the key authorization. ACME CAs are expected to update
// their implementations to use the newer version, TLS-SNI-02.
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name of the client hello matches exactly the returned name value.
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
	ka, err := keyAuth(c.Key.Public(), token)
	if err != nil {
		return tls.Certificate{}, "", err
	}
	b := sha256.Sum256([]byte(ka))
	h := hex.EncodeToString(b[:])
	name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:])
	cert, err = tlsChallengeCert([]string{name}, opt)
	if err != nil {
		return tls.Certificate{}, "", err
	}
	return cert, name, nil
}

// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-SNI-02 see
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the client hello matches exactly the returned name value.
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
	b := sha256.Sum256([]byte(token))
	h := hex.EncodeToString(b[:])
	sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:])

	ka, err := keyAuth(c.Key.Public(), token)
	if err != nil {
		return tls.Certificate{}, "", err
	}
	b = sha256.Sum256([]byte(ka))
	h = hex.EncodeToString(b[:])
	sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:])

	cert, err = tlsChallengeCert([]string{sanA, sanB}, opt)
	if err != nil {
		return tls.Certificate{}, "", err
	}
	return cert, sanA, nil
}

// doReg sends all types of registration requests.
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//
// A non-nil acct argument indicates whether the intention is to mutate data
// of the Account. Only Contact and Agreement of its fields are used
// in such cases.
func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) {
	req := struct {
		Resource  string   `json:"resource"`
		Contact   []string `json:"contact,omitempty"`
		Agreement string   `json:"agreement,omitempty"`
	}{
		Resource: typ,
	}
	if acct != nil {
		req.Contact = acct.Contact
		req.Agreement = acct.AgreedTerms
	}
	res, err := c.retryPostJWS(ctx, c.Key, url, req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode < 200 || res.StatusCode > 299 {
		return nil, responseError(res)
	}

	var v struct {
		Contact        []string
		Agreement      string
		Authorizations string
		Certificates   string
	}
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, fmt.Errorf("acme: invalid response: %v", err)
	}
	var tos string
	if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 {
		tos = v[0]
	}
	var authz string
	if v := linkHeader(res.Header, "next"); len(v) > 0 {
		authz = v[0]
	}
	return &Account{
		URI:            res.Header.Get("Location"),
		Contact:        v.Contact,
		AgreedTerms:    v.Agreement,
		CurrentTerms:   tos,
		Authz:          authz,
		Authorizations: v.Authorizations,
		Certificates:   v.Certificates,
	}, nil
}

// retryPostJWS will retry calls to postJWS if there is a badNonce error,
// clearing the stored nonces after each error.
// If the response was 4XX-5XX, then responseError is called on the body,
// the body is closed, and the error returned.
func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
	sleep := sleeper(ctx)
	for {
		res, err := c.postJWS(ctx, key, url, body)
		if err != nil {
			return nil, err
		}
		// handle errors 4XX-5XX with responseError
		if res.StatusCode >= 400 && res.StatusCode <= 599 {
			err := responseError(res)
			res.Body.Close()
			// according to spec badNonce is urn:ietf:params:acme:error:badNonce
			// however, acme servers in the wild return their version of the error
			// https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
			if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") {
				// clear any nonces that we might've stored that might now be
				// considered bad
				c.clearNonces()
				retry := res.Header.Get("retry-after")
				if err := sleep(retry, 1); err != nil {
					return nil, err
				}
				continue
			}
			return nil, err
		}
		return res, nil
	}
}

// postJWS signs the body with the given key and POSTs it to the provided url.
// The body argument must be JSON-serializable.
func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
	nonce, err := c.popNonce(ctx, url)
	if err != nil {
		return nil, err
	}
	b, err := jwsEncodeJSON(body, key, nonce)
	if err != nil {
		return nil, err
	}
	res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b))
	if err != nil {
		return nil, err
	}
	c.addNonce(res.Header)
	return res, nil
}

// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from the given URL.
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
	c.noncesMu.Lock()
	defer c.noncesMu.Unlock()
	if len(c.nonces) == 0 {
		return c.fetchNonce(ctx, url)
	}
	var nonce string
	for nonce = range c.nonces {
		delete(c.nonces, nonce)
		break
	}
	return nonce, nil
}

// clearNonces clears any stored nonces
func (c *Client) clearNonces() {
	c.noncesMu.Lock()
	defer c.noncesMu.Unlock()
	c.nonces = make(map[string]struct{})
}

// addNonce stores a nonce value found in h (if any) for future use.
func (c *Client) addNonce(h http.Header) {
	v := nonceFromHeader(h)
	if v == "" {
		return
	}
	c.noncesMu.Lock()
	defer c.noncesMu.Unlock()
	if len(c.nonces) >= maxNonces {
		return
	}
	if c.nonces == nil {
		c.nonces = make(map[string]struct{})
	}
	c.nonces[v] = struct{}{}
}

func (c *Client) httpClient() *http.Client {
	if c.HTTPClient != nil {
		return c.HTTPClient
	}
	return http.DefaultClient
}

func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) {
	req, err := http.NewRequest("GET", urlStr, nil)
	if err != nil {
		return nil, err
	}
	return c.do(ctx, req)
}

func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) {
	req, err := http.NewRequest("HEAD", urlStr, nil)
	if err != nil {
		return nil, err
	}
	return c.do(ctx, req)
}

func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest("POST", urlStr, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	return c.do(ctx, req)
}

func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
	res, err := c.httpClient().Do(req.WithContext(ctx))
	if err != nil {
		select {
		case <-ctx.Done():
			// Prefer the unadorned context error.
			// (The acme package had tests assuming this, previously from ctxhttp's
			// behavior, predating net/http supporting contexts natively)
			// TODO(bradfitz): reconsider this in the future. But for now this
			// requires no test updates.
			return nil, ctx.Err()
		default:
			return nil, err
		}
	}
	return res, nil
}

func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
	resp, err := c.head(ctx, url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	nonce := nonceFromHeader(resp.Header)
	if nonce == "" {
		if resp.StatusCode > 299 {
			return "", responseError(resp)
		}
		return "", errors.New("acme: nonce not found")
	}
	return nonce, nil
}

func nonceFromHeader(h http.Header) string {
	return h.Get("Replay-Nonce")
}

func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
	b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
	if err != nil {
		return nil, fmt.Errorf("acme: response stream: %v", err)
	}
	if len(b) > maxCertSize {
		return nil, errors.New("acme: certificate is too big")
	}
	cert := [][]byte{b}
	if !bundle {
		return cert, nil
	}

	// Append CA chain cert(s).
	// At least one is required according to the spec:
	// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
	up := linkHeader(res.Header, "up")
	if len(up) == 0 {
		return nil, errors.New("acme: rel=up link not found")
	}
	if len(up) > maxChainLen {
		return nil, errors.New("acme: rel=up link is too large")
	}
	for _, url := range up {
		cc, err := c.chainCert(ctx, url, 0)
		if err != nil {
			return nil, err
		}
		cert = append(cert, cc...)
	}
	return cert, nil
}

// responseError creates an error of Error type from resp.
func responseError(resp *http.Response) error {
	// don't care if ReadAll returns an error:
	// json.Unmarshal will fail in that case anyway
	b, _ := ioutil.ReadAll(resp.Body)
	e := &wireError{Status: resp.StatusCode}
	if err := json.Unmarshal(b, e); err != nil {
		// this is not a regular error response:
		// populate detail with anything we received,
		// e.Status will already contain HTTP response code value
		e.Detail = string(b)
		if e.Detail == "" {
			e.Detail = resp.Status
		}
	}
	return e.error(resp.Header)
}

// chainCert fetches CA certificate chain recursively by following "up" links.
// Each recursive call increments the depth by 1, resulting in an error
// if the recursion level reaches maxChainLen.
//
// First chainCert call starts with depth of 0.
func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
	if depth >= maxChainLen {
		return nil, errors.New("acme: certificate chain is too deep")
	}

	res, err := c.get(ctx, url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return nil, responseError(res)
	}
	b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
	if err != nil {
		return nil, err
	}
	if len(b) > maxCertSize {
		return nil, errors.New("acme: certificate is too big")
	}
	chain := [][]byte{b}

	uplink := linkHeader(res.Header, "up")
	if len(uplink) > maxChainLen {
		return nil, errors.New("acme: certificate chain is too large")
	}
	for _, up := range uplink {
		cc, err := c.chainCert(ctx, up, depth+1)
		if err != nil {
			return nil, err
		}
		chain = append(chain, cc...)
	}

	return chain, nil
}

// linkHeader returns URI-Reference values of all Link headers
// with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
func linkHeader(h http.Header, rel string) []string {
	var links []string
	for _, v := range h["Link"] {
		parts := strings.Split(v, ";")
		for _, p := range parts {
			p = strings.TrimSpace(p)
			if !strings.HasPrefix(p, "rel=") {
				continue
			}
			if v := strings.Trim(p[4:], `"`); v == rel {
				links = append(links, strings.Trim(parts[0], "<>"))
			}
		}
	}
	return links
}

// sleeper returns a function that accepts the Retry-After HTTP header value
// and an increment that's used with backoff to increasingly sleep on
// consecutive calls until the context is done. If the Retry-After header
// cannot be parsed, then backoff is used with a maximum sleep time of 10
// seconds.
func sleeper(ctx context.Context) func(ra string, inc int) error {
	var count int
	return func(ra string, inc int) error {
		count += inc
		d := backoff(count, 10*time.Second)
		d = retryAfter(ra, d)
		wakeup := time.NewTimer(d)
		defer wakeup.Stop()
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-wakeup.C:
			return nil
		}
	}
}

// retryAfter parses a Retry-After HTTP header value,
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
// It returns d if v cannot be parsed.
func retryAfter(v string, d time.Duration) time.Duration {
	if i, err := strconv.Atoi(v); err == nil {
		return time.Duration(i) * time.Second
	}
	t, err := http.ParseTime(v)
	if err != nil {
		return d
	}
	return t.Sub(timeNow())
}

// backoff computes a duration after which an n+1 retry iteration should occur
// using truncated exponential backoff algorithm.
//
// The n argument is always bounded between 0 and 30.
// The max argument defines upper bound for the returned value.
func backoff(n int, max time.Duration) time.Duration {
	if n < 0 {
		n = 0
	}
	if n > 30 {
		n = 30
	}
	var d time.Duration
	if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
		d = time.Duration(x.Int64()) * time.Millisecond
	}
	d += time.Duration(1<<uint(n)) * time.Second
	if d > max {
		return max
	}
	return d
}

// keyAuth generates a key authorization string for a given token.
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
	th, err := JWKThumbprint(pub)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s.%s", token, th), nil
}

// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
// with the given SANs and auto-generated public/private key pair.
// To create a cert with a custom key pair, specify WithKey option.
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
	var (
		key  crypto.Signer
		tmpl *x509.Certificate
	)
	for _, o := range opt {
		switch o := o.(type) {
		case *certOptKey:
			if key != nil {
				return tls.Certificate{}, errors.New("acme: duplicate key option")
			}
			key = o.key
		case *certOptTemplate:
			var t = *(*x509.Certificate)(o) // shallow copy is ok
			tmpl = &t
		default:
			// package's fault, if we let this happen:
			panic(fmt.Sprintf("unsupported option type %T", o))
		}
	}
	if key == nil {
		var err error
		if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
			return tls.Certificate{}, err
		}
	}
	if tmpl == nil {
		tmpl = &x509.Certificate{
			SerialNumber:          big.NewInt(1),
			NotBefore:             time.Now(),
			NotAfter:              time.Now().Add(24 * time.Hour),
			BasicConstraintsValid: true,
			KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		}
	}
	tmpl.DNSNames = san

	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
	if err != nil {
		return tls.Certificate{}, err
	}
	return tls.Certificate{
		Certificate: [][]byte{der},
		PrivateKey:  key,
	}, nil
}

// encodePEM returns b encoded as PEM with block of type typ.
func encodePEM(typ string, b []byte) []byte {
	pb := &pem.Block{Type: typ, Bytes: b}
	return pem.EncodeToMemory(pb)
}

// timeNow is useful for testing for fixed current time.
var timeNow = time.Now


import { createElement } from 'rax';
import { action } from '@storybook/addon-actions';

import Text from 'rax-text';

export default {
  title: 'Addon/addon-actions',
};

export const WithText = () => (
  <button onClick={action('clicked')} type="button">
    Hello Button
  </button>
);

WithText.storyName = 'with text';

export const WithSomeEmoji = () => (
  <button onClick={action('clicked')} type="button">
    <Text role="img" aria-label="so cool">
      😀 😎 👍 💯
    </Text>
  </button>
);

WithSomeEmoji.storyName = 'with some emoji';


<!DOCTYPE html>

    <form class="form-horizontal m" id="form-resource-add" th:object="${resource}">
        <input name="id"   type="hidden" th:field="*{id}"   />
        <input id="treeId" name="parentId" type="hidden" th:field="*{parentId}" />

            菜单类型：

                 <input type="radio" name="type" th:field="*{type}" value="0" /> 目录
                 <input type="radio" name="type" th:field="*{type}" value="1" checked="checked" /> 菜单
                 <input type="radio" name="type" th:field="*{type}" value="2" /> 按钮

            资源名称：

                <input class="form-control" type="text" th:field="*{name}" name="name" id="name" required="required"/>

            上级菜单：

                <input class="form-control" type="text" onclick="selectResourceTree()" id="treeName" readonly="true" required="required" th:value="${resource.parentname==null?'无':resource.parentname}"/>

            排序：

                <input class="form-control" type="text" name="priority" id="priority" th:field="*{priority}" required="required"/>

            图标：

                <input class="form-control" type="text" name="icon" id="icon" th:field="*{icon}"/>

            菜单状态：

                 <input type="radio" name="status" value="1" id="status" checked="checked" th:field="*{status}" /> 有效
                 <input type="radio" name="status" value="2"  id="status" th:field="*{status}"/> 无效

            权限访问路径：

                <input class="form-control" type="text" name="url" id="url" th:field="*{url}" required="required"/>

            权限标识：

                <input class="form-control" type="text" name="permission" id="permission" th:field="*{permission}" required="required"/>

            资源描述：

                <input class="form-control" type="text" name="description" id="description" th:field="*{description}"/>

            (默认自动获取)uuid：

                <input class="form-control" type="text" name="resourceId" id="resourceId" th:field="*{resourceId}"/>

    </form>


one
two
three
four


salt.states.glassfish module
============================

.. automodule:: salt.states.glassfish
    :members:


package packd

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/pkg/errors"
)

var _ File = &virtualFile{}
var _ io.Reader = &virtualFile{}
var _ io.Writer = &virtualFile{}
var _ fmt.Stringer = &virtualFile{}

type virtualFile struct {
	buf      *bytes.Buffer
	name     string
	info     fileInfo
	original []byte
}

func (f virtualFile) Name() string {
	return f.name
}

func (f virtualFile) Seek(offset int64, whence int) (int64, error) {
	return -1, nil
}

func (f virtualFile) FileInfo() (os.FileInfo, error) {
	return f.info, nil
}

func (f *virtualFile) Close() error {
	return nil
}

func (f virtualFile) Readdir(count int) ([]os.FileInfo, error) {
	return []os.FileInfo{f.info}, nil
}

func (f virtualFile) Stat() (os.FileInfo, error) {
	return f.info, nil
}

func (f virtualFile) String() string {
	return string(f.original)
}

func (s *virtualFile) Read(p []byte) (int, error) {
	i, err := s.buf.Read(p)

	if i == 0 || err == io.EOF {
		s.buf = bytes.NewBuffer(s.original)
	}
	return i, err
}

func (s *virtualFile) Write(p []byte) (int, error) {
	bb := &bytes.Buffer{}
	i, err := bb.Write(p)
	if err != nil {
		return i, errors.WithStack(err)
	}
	s.buf = bb
	s.original = bb.Bytes()
	s.info = fileInfo{
		Path:     s.name,
		Contents: bb.Bytes(),
		size:     int64(bb.Len()),
		modTime:  time.Now(),
	}
	return i, nil
}

// NewDir returns a new "virtual" file
func NewFile(name string, r io.Reader) (File, error) {
	bb := &bytes.Buffer{}
	if r != nil {
		io.Copy(bb, r)
	}
	return &virtualFile{
		buf:      bb,
		name:     name,
		original: bb.Bytes(),
		info: fileInfo{
			Path:     name,
			Contents: bb.Bytes(),
			size:     int64(bb.Len()),
			modTime:  time.Now(),
		},
	}, nil
}

// NewDir returns a new "virtual" directory
func NewDir(name string) (File, error) {
	bb := &bytes.Buffer{}
	return &virtualFile{
		buf:  bb,
		name: name,
		info: fileInfo{
			Path:     name,
			Contents: bb.Bytes(),
			size:     int64(bb.Len()),
			modTime:  time.Now(),
			isDir:    true,
		},
	}, nil
}


from ctypes import *
from ctypes import util
from core import *

security = cdll.LoadLibrary(util.find_library("Security"))

class Keychain:
    def __init__(self, service=None):
        if service:
            self.service = c_char_p(service)
            self.serviceLen = c_ulong(len(service))
        else:
            self.service = c_char_p(bundle())
            self.serviceLen = c_ulong(len(bundle()))

    def storePassword(self, account, password):
        acctLen = c_ulong(len(account))
        pwLen = c_ulong(len(password))
        acctData = c_char_p(account)
        pwData = create_string_buffer(password)
        security.SecKeychainAddGenericPassword(
                    None,
                    self.serviceLen,
                    self.service,
                    acctLen,
                    acctData,
                    pwLen,
                    pwData,
                    None
            )

    def retrievePassword(self, account):
        acctLen = c_ulong(len(account))
        acctData = c_char_p(account)
        pwLen = pointer(c_ulong())
        pwData = pointer(c_char_p())

        security.SecKeychainFindGenericPassword(
                    None,
                    self.serviceLen,
                    self.service,
                    acctLen,
                    acctData,
                    pwLen,
                    pwData,
                    None
            )

        intendedLen = pwLen.contents.value
        return pwData.contents.value[0:intendedLen]

    def modifyPassword(self, account, newPassword):
        acctLen = c_ulong(len(account))
        newPwLen = c_ulong(len(newPassword))
        itemRef = pointer(c_void_p())
        newPwData = create_string_buffer(newPassword)

        security.SecKeychainFindGenericPassword(
                    None,
                    self.serviceLen,
                    self.service,
                    acctLen,
                    account,
                    None,
                    None,
                    itemRef
            )
        security.SecKeychainItemModifyAttributesAndData(
                itemRef.contents,
                None,
                newPwLen,
                newPwData
        )

    def deletePassword(self, account):
        acctLen = c_ulong(len(account) + 1)
        itemRef = c_void_p()
        security.SecKeychainFindGenericPassword(
                    None,
                    self.serviceLen,
                    self.service,
                    acctLen,
                    account,
                    None,
                    None,
                    byref(itemRef)
            )
        security.SecKeychainItemDelete(itemRef)


"use strict";

var helpers = require("../../helpers/helpers");

exports["Pacific/Saipan"] = {

	"guess:by:offset" : helpers.makeTestGuess("Pacific/Saipan", { offset: true, expect: "Australia/Brisbane" }),

	"guess:by:abbr" : helpers.makeTestGuess("Pacific/Saipan", { abbr: true, expect: "Pacific/Guam" }),

	"1941" : helpers.makeTestYear("Pacific/Saipan", [
		["1941-12-09T13:59:59+00:00", "23:59:59", "GST", -600],
		["1941-12-09T14:00:00+00:00", "23:00:00", "+09", -540]
	]),

	"1944" : helpers.makeTestYear("Pacific/Saipan", [
		["1944-07-30T14:59:59+00:00", "23:59:59", "+09", -540],
		["1944-07-30T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1959" : helpers.makeTestYear("Pacific/Saipan", [
		["1959-06-26T15:59:59+00:00", "01:59:59", "GST", -600],
		["1959-06-26T16:00:00+00:00", "03:00:00", "GDT", -660]
	]),

	"1961" : helpers.makeTestYear("Pacific/Saipan", [
		["1961-01-28T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1961-01-28T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1967" : helpers.makeTestYear("Pacific/Saipan", [
		["1967-08-31T15:59:59+00:00", "01:59:59", "GST", -600],
		["1967-08-31T16:00:00+00:00", "03:00:00", "GDT", -660]
	]),

	"1969" : helpers.makeTestYear("Pacific/Saipan", [
		["1969-01-25T13:00:59+00:00", "00:00:59", "GDT", -660],
		["1969-01-25T13:01:00+00:00", "23:01:00", "GST", -600],
		["1969-06-21T15:59:59+00:00", "01:59:59", "GST", -600],
		["1969-06-21T16:00:00+00:00", "03:00:00", "GDT", -660],
		["1969-08-30T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1969-08-30T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1970" : helpers.makeTestYear("Pacific/Saipan", [
		["1970-04-25T15:59:59+00:00", "01:59:59", "GST", -600],
		["1970-04-25T16:00:00+00:00", "03:00:00", "GDT", -660],
		["1970-09-05T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1970-09-05T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1971" : helpers.makeTestYear("Pacific/Saipan", [
		["1971-04-24T15:59:59+00:00", "01:59:59", "GST", -600],
		["1971-04-24T16:00:00+00:00", "03:00:00", "GDT", -660],
		["1971-09-04T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1971-09-04T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1973" : helpers.makeTestYear("Pacific/Saipan", [
		["1973-12-15T15:59:59+00:00", "01:59:59", "GST", -600],
		["1973-12-15T16:00:00+00:00", "03:00:00", "GDT", -660]
	]),

	"1974" : helpers.makeTestYear("Pacific/Saipan", [
		["1974-02-23T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1974-02-23T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"1976" : helpers.makeTestYear("Pacific/Saipan", [
		["1976-05-25T15:59:59+00:00", "01:59:59", "GST", -600],
		["1976-05-25T16:00:00+00:00", "03:00:00", "GDT", -660],
		["1976-08-21T15:00:59+00:00", "02:00:59", "GDT", -660],
		["1976-08-21T15:01:00+00:00", "01:01:00", "GST", -600]
	]),

	"1977" : helpers.makeTestYear("Pacific/Saipan", [
		["1977-04-23T15:59:59+00:00", "01:59:59", "GST", -600],
		["1977-04-23T16:00:00+00:00", "03:00:00", "GDT", -660],
		["1977-08-27T14:59:59+00:00", "01:59:59", "GDT", -660],
		["1977-08-27T15:00:00+00:00", "01:00:00", "GST", -600]
	]),

	"2000" : helpers.makeTestYear("Pacific/Saipan", [
		["2000-12-22T13:59:59+00:00", "23:59:59", "GST", -600],
		["2000-12-22T14:00:00+00:00", "00:00:00", "ChST", -600]
	])
};


-- Test encoding/decoding/transcoding of everything concerning the BIT STRING.
T ::= BIT STRING { foo(0) }
T ::= BIT STRING { foo(0) } (SIZE(0..1))
T ::= BIT STRING { foo(0) } (SIZE(1))
T ::= BIT STRING { foo(0) } (SIZE(1..MAX))
T ::= BIT STRING { foo(0) } (SIZE(1..2))
T ::= BIT STRING { foo(0) } (SIZE(2))
T ::= BIT STRING { foo(0) } (SIZE(2,...))
T ::= BIT STRING { foo(0), bar(65535) }
T ::= BIT STRING { foo(0), bar(65535) } (SIZE(0..16))
T ::= BIT STRING { foo(0), bar(65535) } (SIZE(16))
T ::= BIT STRING { foo(0), bar(2147483647) }
T ::= BIT STRING { foo(0), bar(2147483647) } (SIZE(1..MAX))
T ::= BIT STRING { foo(2147483648), bar(0), baz(2147483647) }
T ::= BIT STRING { foo(2147483648), baz(2147483647) }
T ::= BIT STRING { foo(2147483648), baz(2147483647) } (SIZE(32))
T ::= BIT STRING { foo(2147483648), baz(2147483647) } (SIZE(31,...))
T ::= BIT STRING { foo(2147483648), baz(2147483647) } (SIZE(0..32))
T ::= BIT STRING { foo(2147483648), baz(2147483647) } (SIZE(0..31,...))
T ::= BIT STRING { foo(0) } (SIZE(65535))       -- RMAX=65540
T ::= BIT STRING { foo(0) } (SIZE(65536))       -- RMAX=65540
T ::= BIT STRING { foo(0) } (SIZE(65537))       -- RMAX=65540
T ::= BIT STRING { foo(0) } (SIZE(65535,...))   -- RMAX=65540
T ::= BIT STRING { foo(0) } (SIZE(65536,...))   -- RMAX=65540
T ::= BIT STRING { foo(0) } (SIZE(65537,...))   -- RMAX=65540


// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html

// +build amd64,!gccgo,!appengine

// func square(out, in *[5]uint64)
TEXT ·square(SB),7,$96-16
	MOVQ out+0(FP), DI
	MOVQ in+8(FP), SI

	MOVQ SP,R11
	MOVQ $31,CX
	NOTQ CX
	ANDQ CX,SP
	ADDQ $32, SP

	MOVQ R11,0(SP)
	MOVQ R12,8(SP)
	MOVQ R13,16(SP)
	MOVQ R14,24(SP)
	MOVQ R15,32(SP)
	MOVQ BX,40(SP)
	MOVQ BP,48(SP)
	MOVQ 0(SI),AX
	MULQ 0(SI)
	MOVQ AX,CX
	MOVQ DX,R8
	MOVQ 0(SI),AX
	SHLQ $1,AX
	MULQ 8(SI)
	MOVQ AX,R9
	MOVQ DX,R10
	MOVQ 0(SI),AX
	SHLQ $1,AX
	MULQ 16(SI)
	MOVQ AX,R11
	MOVQ DX,R12
	MOVQ 0(SI),AX
	SHLQ $1,AX
	MULQ 24(SI)
	MOVQ AX,R13
	MOVQ DX,R14
	MOVQ 0(SI),AX
	SHLQ $1,AX
	MULQ 32(SI)
	MOVQ AX,R15
	MOVQ DX,BX
	MOVQ 8(SI),AX
	MULQ 8(SI)
	ADDQ AX,R11
	ADCQ DX,R12
	MOVQ 8(SI),AX
	SHLQ $1,AX
	MULQ 16(SI)
	ADDQ AX,R13
	ADCQ DX,R14
	MOVQ 8(SI),AX
	SHLQ $1,AX
	MULQ 24(SI)
	ADDQ AX,R15
	ADCQ DX,BX
	MOVQ 8(SI),DX
	IMUL3Q $38,DX,AX
	MULQ 32(SI)
	ADDQ AX,CX
	ADCQ DX,R8
	MOVQ 16(SI),AX
	MULQ 16(SI)
	ADDQ AX,R15
	ADCQ DX,BX
	MOVQ 16(SI),DX
	IMUL3Q $38,DX,AX
	MULQ 24(SI)
	ADDQ AX,CX
	ADCQ DX,R8
	MOVQ 16(SI),DX
	IMUL3Q $38,DX,AX
	MULQ 32(SI)
	ADDQ AX,R9
	ADCQ DX,R10
	MOVQ 24(SI),DX
	IMUL3Q $19,DX,AX
	MULQ 24(SI)
	ADDQ AX,R9
	ADCQ DX,R10
	MOVQ 24(SI),DX
	IMUL3Q $38,DX,AX
	MULQ 32(SI)
	ADDQ AX,R11
	ADCQ DX,R12
	MOVQ 32(SI),DX
	IMUL3Q $19,DX,AX
	MULQ 32(SI)
	ADDQ AX,R13
	ADCQ DX,R14
	MOVQ ·REDMASK51(SB),SI
	SHLQ $13,R8:CX
	ANDQ SI,CX
	SHLQ $13,R10:R9
	ANDQ SI,R9
	ADDQ R8,R9
	SHLQ $13,R12:R11
	ANDQ SI,R11
	ADDQ R10,R11
	SHLQ $13,R14:R13
	ANDQ SI,R13
	ADDQ R12,R13
	SHLQ $13,BX:R15
	ANDQ SI,R15
	ADDQ R14,R15
	IMUL3Q $19,BX,DX
	ADDQ DX,CX
	MOVQ CX,DX
	SHRQ $51,DX
	ADDQ R9,DX
	ANDQ SI,CX
	MOVQ DX,R8
	SHRQ $51,DX
	ADDQ R11,DX
	ANDQ SI,R8
	MOVQ DX,R9
	SHRQ $51,DX
	ADDQ R13,DX
	ANDQ SI,R9
	MOVQ DX,AX
	SHRQ $51,DX
	ADDQ R15,DX
	ANDQ SI,AX
	MOVQ DX,R10
	SHRQ $51,DX
	IMUL3Q $19,DX,DX
	ADDQ DX,CX
	ANDQ SI,R10
	MOVQ CX,0(DI)
	MOVQ R8,8(DI)
	MOVQ R9,16(DI)
	MOVQ AX,24(DI)
	MOVQ R10,32(DI)
	MOVQ 0(SP),R11
	MOVQ 8(SP),R12
	MOVQ 16(SP),R13
	MOVQ 24(SP),R14
	MOVQ 32(SP),R15
	MOVQ 40(SP),BX
	MOVQ 48(SP),BP
	MOVQ R11,SP
	MOVQ DI,AX
	MOVQ SI,DX
	RET


var memoizeCapped = require('./_memoizeCapped');

/** Used to match property names within property paths. */
var reLeadingDot = /^\./,
    rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = memoizeCapped(function(string) {
  var result = [];
  if (reLeadingDot.test(string)) {
    result.push('');
  }
  string.replace(rePropName, function(match, number, quote, string) {
    result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

module.exports = stringToPath;