106f6ba60Sopenharmony_ci/* 206f6ba60Sopenharmony_ci * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. 306f6ba60Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 406f6ba60Sopenharmony_ci * you may not use this file except in compliance with the License. 506f6ba60Sopenharmony_ci * You may obtain a copy of the License at 606f6ba60Sopenharmony_ci * 706f6ba60Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 806f6ba60Sopenharmony_ci * 906f6ba60Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 1006f6ba60Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 1106f6ba60Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1206f6ba60Sopenharmony_ci * See the License for the specific language governing permissions and 1306f6ba60Sopenharmony_ci * limitations under the License. 1406f6ba60Sopenharmony_ci */ 1506f6ba60Sopenharmony_ci 1606f6ba60Sopenharmony_ci#include "ringbuffer.h" 1706f6ba60Sopenharmony_ci 1806f6ba60Sopenharmony_ci#include <memory> 1906f6ba60Sopenharmony_ci#include <sys/uio.h> 2006f6ba60Sopenharmony_ci#include <securec.h> 2106f6ba60Sopenharmony_ci#include <cstring> 2206f6ba60Sopenharmony_ci#include <iostream> 2306f6ba60Sopenharmony_ciRingBuffer::RingBuffer(const std::size_t bufSize, const enum MemAlignShift shift) 2406f6ba60Sopenharmony_ci : bufSize_ {bufSize}, 2506f6ba60Sopenharmony_ci alignShift_ {shift} 2606f6ba60Sopenharmony_ci{ 2706f6ba60Sopenharmony_ci if (bufSize_ <= DEFAULT_SIZE) { 2806f6ba60Sopenharmony_ci bufSize_ = DEFAULT_SIZE; 2906f6ba60Sopenharmony_ci } 3006f6ba60Sopenharmony_ci switch (shift) { 3106f6ba60Sopenharmony_ci case B_ALIGN_SHIFT: { 3206f6ba60Sopenharmony_ci bufSize_ = (bufSize_ >> B_ALIGN_SHIFT); 3306f6ba60Sopenharmony_ci uint16_t *temp = new(std::nothrow) uint16_t[bufSize_]; 3406f6ba60Sopenharmony_ci if (temp != nullptr) { 3506f6ba60Sopenharmony_ci buffer_ = reinterpret_cast<char *>(temp); 3606f6ba60Sopenharmony_ci } 3706f6ba60Sopenharmony_ci bufSize_ = (bufSize_ << B_ALIGN_SHIFT); 3806f6ba60Sopenharmony_ci break; 3906f6ba60Sopenharmony_ci } 4006f6ba60Sopenharmony_ci case H_ALIGN_SHIFT: { 4106f6ba60Sopenharmony_ci bufSize_ = (bufSize_ >> H_ALIGN_SHIFT); 4206f6ba60Sopenharmony_ci uint16_t *temp = new(std::nothrow) uint16_t[bufSize_]; 4306f6ba60Sopenharmony_ci if (temp != nullptr) { 4406f6ba60Sopenharmony_ci buffer_ = reinterpret_cast<char *>(temp); 4506f6ba60Sopenharmony_ci } 4606f6ba60Sopenharmony_ci bufSize_ = (bufSize_ << H_ALIGN_SHIFT); 4706f6ba60Sopenharmony_ci break; 4806f6ba60Sopenharmony_ci } 4906f6ba60Sopenharmony_ci case W_ALIGN_SHIFT: { 5006f6ba60Sopenharmony_ci bufSize_ = (bufSize_ >> W_ALIGN_SHIFT); 5106f6ba60Sopenharmony_ci uint32_t *temp = new(std::nothrow) uint32_t[bufSize_]; 5206f6ba60Sopenharmony_ci if (temp != nullptr) { 5306f6ba60Sopenharmony_ci buffer_ = reinterpret_cast<char *>(temp); 5406f6ba60Sopenharmony_ci } 5506f6ba60Sopenharmony_ci bufSize_ = (bufSize_ << W_ALIGN_SHIFT); 5606f6ba60Sopenharmony_ci break; 5706f6ba60Sopenharmony_ci } 5806f6ba60Sopenharmony_ci case D_ALIGN_SHIFT: { 5906f6ba60Sopenharmony_ci bufSize_ = (bufSize_ >> D_ALIGN_SHIFT); 6006f6ba60Sopenharmony_ci uint64_t *temp = new(std::nothrow) uint64_t[bufSize_]; 6106f6ba60Sopenharmony_ci if (temp != nullptr) { 6206f6ba60Sopenharmony_ci buffer_ = reinterpret_cast<char *>(temp); 6306f6ba60Sopenharmony_ci } 6406f6ba60Sopenharmony_ci bufSize_ = (bufSize_ << D_ALIGN_SHIFT); 6506f6ba60Sopenharmony_ci break; 6606f6ba60Sopenharmony_ci } 6706f6ba60Sopenharmony_ci } 6806f6ba60Sopenharmony_ci} 6906f6ba60Sopenharmony_ci 7006f6ba60Sopenharmony_cissize_t RingBuffer::Read(const int fd, const std::size_t len) 7106f6ba60Sopenharmony_ci{ 7206f6ba60Sopenharmony_ci if (fd < 0) { 7306f6ba60Sopenharmony_ci return -1; 7406f6ba60Sopenharmony_ci } 7506f6ba60Sopenharmony_ci if (len == 0) { 7606f6ba60Sopenharmony_ci return 0; 7706f6ba60Sopenharmony_ci } 7806f6ba60Sopenharmony_ci constexpr std::size_t numDests {2}; 7906f6ba60Sopenharmony_ci struct iovec destBufs[numDests]; 8006f6ba60Sopenharmony_ci // resize if free space is not big enough 8106f6ba60Sopenharmony_ci std::lock_guard<std::mutex> lk {mtx_}; 8206f6ba60Sopenharmony_ci while (len >= FreeSize()) { 8306f6ba60Sopenharmony_ci // the equal sign makes sure the buffer will not be fully filled 8406f6ba60Sopenharmony_ci if (Resize() != 0) { 8506f6ba60Sopenharmony_ci return -1; 8606f6ba60Sopenharmony_ci } 8706f6ba60Sopenharmony_ci } 8806f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 8906f6ba60Sopenharmony_ci return -1; 9006f6ba60Sopenharmony_ci } 9106f6ba60Sopenharmony_ci // now we have enough free space to read in from fd 9206f6ba60Sopenharmony_ci destBufs[0].iov_base = buffer_ + tail_; 9306f6ba60Sopenharmony_ci if (tail_ + len < bufSize_) { 9406f6ba60Sopenharmony_ci // continuous free space 9506f6ba60Sopenharmony_ci destBufs[0].iov_len = len; 9606f6ba60Sopenharmony_ci destBufs[1].iov_base = buffer_ + tail_ + len; 9706f6ba60Sopenharmony_ci destBufs[1].iov_len = 0; 9806f6ba60Sopenharmony_ci } else { 9906f6ba60Sopenharmony_ci // free space splitted 10006f6ba60Sopenharmony_ci destBufs[0].iov_len = bufSize_ - tail_; 10106f6ba60Sopenharmony_ci destBufs[1].iov_base = buffer_; 10206f6ba60Sopenharmony_ci destBufs[1].iov_len = len + tail_ - bufSize_; 10306f6ba60Sopenharmony_ci } 10406f6ba60Sopenharmony_ci ssize_t ret = readv(fd, destBufs, numDests); 10506f6ba60Sopenharmony_ci if (ret != -1) { 10606f6ba60Sopenharmony_ci // update buffer status 10706f6ba60Sopenharmony_ci tail_ += static_cast<std::size_t>(ret); 10806f6ba60Sopenharmony_ci while (tail_ >= bufSize_) { 10906f6ba60Sopenharmony_ci tail_ -= bufSize_; 11006f6ba60Sopenharmony_ci } 11106f6ba60Sopenharmony_ci } 11206f6ba60Sopenharmony_ci return ret; 11306f6ba60Sopenharmony_ci} 11406f6ba60Sopenharmony_ci 11506f6ba60Sopenharmony_cissize_t RingBuffer::Write(const int fd, std::size_t len) 11606f6ba60Sopenharmony_ci{ 11706f6ba60Sopenharmony_ci if (fd < 0) { 11806f6ba60Sopenharmony_ci return -1; 11906f6ba60Sopenharmony_ci } 12006f6ba60Sopenharmony_ci constexpr std::size_t numSrcs {2}; 12106f6ba60Sopenharmony_ci struct iovec srcBufs[numSrcs]; 12206f6ba60Sopenharmony_ci std::lock_guard<std::mutex> lk {mtx_}; 12306f6ba60Sopenharmony_ci std::size_t dataSize = DataSize(); 12406f6ba60Sopenharmony_ci if (dataSize < len) { 12506f6ba60Sopenharmony_ci len = dataSize; 12606f6ba60Sopenharmony_ci } 12706f6ba60Sopenharmony_ci if (len == 0) { 12806f6ba60Sopenharmony_ci return 0; 12906f6ba60Sopenharmony_ci } 13006f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 13106f6ba60Sopenharmony_ci return -1; 13206f6ba60Sopenharmony_ci } 13306f6ba60Sopenharmony_ci // now we are sure there is at least 'len' bytes data in the buffer 13406f6ba60Sopenharmony_ci srcBufs[0].iov_base = buffer_ + head_; 13506f6ba60Sopenharmony_ci if (head_ + len > bufSize_) { 13606f6ba60Sopenharmony_ci // data splitted 13706f6ba60Sopenharmony_ci srcBufs[0].iov_len = bufSize_ - head_; 13806f6ba60Sopenharmony_ci srcBufs[1].iov_base = buffer_; 13906f6ba60Sopenharmony_ci srcBufs[1].iov_len = len + head_- bufSize_; 14006f6ba60Sopenharmony_ci } else { 14106f6ba60Sopenharmony_ci // continuous data 14206f6ba60Sopenharmony_ci srcBufs[0].iov_len = len; 14306f6ba60Sopenharmony_ci srcBufs[1].iov_base = buffer_ + head_ + len; 14406f6ba60Sopenharmony_ci srcBufs[1].iov_len = 0; 14506f6ba60Sopenharmony_ci } 14606f6ba60Sopenharmony_ci ssize_t ret = writev(fd, srcBufs, numSrcs); 14706f6ba60Sopenharmony_ci if (ret != -1) { 14806f6ba60Sopenharmony_ci // update buffer status 14906f6ba60Sopenharmony_ci head_ += static_cast<std::size_t>(ret); 15006f6ba60Sopenharmony_ci while (head_ >= bufSize_) { 15106f6ba60Sopenharmony_ci head_ -= bufSize_; 15206f6ba60Sopenharmony_ci } 15306f6ba60Sopenharmony_ci } 15406f6ba60Sopenharmony_ci return ret; 15506f6ba60Sopenharmony_ci} 15606f6ba60Sopenharmony_ci 15706f6ba60Sopenharmony_cistd::size_t RingBuffer::Get(char* dest, const std::size_t len) 15806f6ba60Sopenharmony_ci{ 15906f6ba60Sopenharmony_ci if (dest == nullptr) { 16006f6ba60Sopenharmony_ci return 0; 16106f6ba60Sopenharmony_ci } 16206f6ba60Sopenharmony_ci if (len == 0) { 16306f6ba60Sopenharmony_ci return 0; 16406f6ba60Sopenharmony_ci } 16506f6ba60Sopenharmony_ci std::lock_guard<std::mutex> lk {mtx_}; 16606f6ba60Sopenharmony_ci auto dataSize = DataSize(); 16706f6ba60Sopenharmony_ci if (len > dataSize) { 16806f6ba60Sopenharmony_ci return 0; 16906f6ba60Sopenharmony_ci } 17006f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 17106f6ba60Sopenharmony_ci return -1; 17206f6ba60Sopenharmony_ci } 17306f6ba60Sopenharmony_ci if (head_ + len > bufSize_) { 17406f6ba60Sopenharmony_ci // data splitted 17506f6ba60Sopenharmony_ci if (memcpy_s(dest, len, buffer_ + head_, bufSize_ - head_) != EOK) { 17606f6ba60Sopenharmony_ci return 0; 17706f6ba60Sopenharmony_ci } 17806f6ba60Sopenharmony_ci if (memcpy_s(dest + bufSize_ - head_, len + head_ - bufSize_, buffer_, len + head_ - bufSize_) != EOK) { 17906f6ba60Sopenharmony_ci return 0; 18006f6ba60Sopenharmony_ci } 18106f6ba60Sopenharmony_ci } else { 18206f6ba60Sopenharmony_ci if (memcpy_s(dest, len, buffer_ + head_, len) != EOK) { 18306f6ba60Sopenharmony_ci return 0; 18406f6ba60Sopenharmony_ci } 18506f6ba60Sopenharmony_ci } 18606f6ba60Sopenharmony_ci // update buffer status 18706f6ba60Sopenharmony_ci head_ += len; 18806f6ba60Sopenharmony_ci while (head_ >= bufSize_) { 18906f6ba60Sopenharmony_ci head_ -= bufSize_; 19006f6ba60Sopenharmony_ci } 19106f6ba60Sopenharmony_ci return len; 19206f6ba60Sopenharmony_ci} 19306f6ba60Sopenharmony_ci 19406f6ba60Sopenharmony_ciint RingBuffer::Put(const char* str, const std::size_t len) 19506f6ba60Sopenharmony_ci{ 19606f6ba60Sopenharmony_ci if (str == nullptr) { 19706f6ba60Sopenharmony_ci return -1; 19806f6ba60Sopenharmony_ci } 19906f6ba60Sopenharmony_ci if (len == 0) { 20006f6ba60Sopenharmony_ci return 0; 20106f6ba60Sopenharmony_ci } 20206f6ba60Sopenharmony_ci // resize if free space is not big enough 20306f6ba60Sopenharmony_ci std::lock_guard<std::mutex> lk {mtx_}; 20406f6ba60Sopenharmony_ci while (len >= FreeSize()) { 20506f6ba60Sopenharmony_ci // the equal sign makes sure the buffer will not be fully filled 20606f6ba60Sopenharmony_ci if (Resize() != 0) { 20706f6ba60Sopenharmony_ci return -1; 20806f6ba60Sopenharmony_ci } 20906f6ba60Sopenharmony_ci } 21006f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 21106f6ba60Sopenharmony_ci return -1; 21206f6ba60Sopenharmony_ci } 21306f6ba60Sopenharmony_ci if (tail_ + len < bufSize_) { 21406f6ba60Sopenharmony_ci // continuous free space 21506f6ba60Sopenharmony_ci if (memcpy_s(buffer_ + tail_, bufSize_ - tail_, str, len) != EOK) { 21606f6ba60Sopenharmony_ci return -1; 21706f6ba60Sopenharmony_ci } 21806f6ba60Sopenharmony_ci } else { 21906f6ba60Sopenharmony_ci // splitted free space 22006f6ba60Sopenharmony_ci if (memcpy_s(buffer_ + tail_, bufSize_ - tail_, str, bufSize_ - tail_) != EOK) { 22106f6ba60Sopenharmony_ci return -1; 22206f6ba60Sopenharmony_ci } 22306f6ba60Sopenharmony_ci if (memcpy_s(buffer_, bufSize_, str + bufSize_ - tail_, len + tail_ - bufSize_) != EOK) { 22406f6ba60Sopenharmony_ci return -1; 22506f6ba60Sopenharmony_ci } 22606f6ba60Sopenharmony_ci } 22706f6ba60Sopenharmony_ci // update buffer status 22806f6ba60Sopenharmony_ci tail_ += len; 22906f6ba60Sopenharmony_ci while (tail_ >= bufSize_) { 23006f6ba60Sopenharmony_ci tail_ -= bufSize_; 23106f6ba60Sopenharmony_ci } 23206f6ba60Sopenharmony_ci return len; 23306f6ba60Sopenharmony_ci} 23406f6ba60Sopenharmony_ci 23506f6ba60Sopenharmony_ciint RingBuffer::Put(const std::string& str) 23606f6ba60Sopenharmony_ci{ 23706f6ba60Sopenharmony_ci if (str.empty()) { 23806f6ba60Sopenharmony_ci return -1; 23906f6ba60Sopenharmony_ci } 24006f6ba60Sopenharmony_ci std::size_t len = str.length(); 24106f6ba60Sopenharmony_ci if (len == 0) { 24206f6ba60Sopenharmony_ci return 0; 24306f6ba60Sopenharmony_ci } 24406f6ba60Sopenharmony_ci // resize if free space is not big enough 24506f6ba60Sopenharmony_ci std::lock_guard<std::mutex> lk {mtx_}; 24606f6ba60Sopenharmony_ci while (len >= FreeSize()) { 24706f6ba60Sopenharmony_ci // the equal sign makes sure the buffer will not be fully filled 24806f6ba60Sopenharmony_ci if (Resize() != 0) { 24906f6ba60Sopenharmony_ci return -1; 25006f6ba60Sopenharmony_ci } 25106f6ba60Sopenharmony_ci } 25206f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 25306f6ba60Sopenharmony_ci return -1; 25406f6ba60Sopenharmony_ci } 25506f6ba60Sopenharmony_ci if (tail_ + len < bufSize_) { 25606f6ba60Sopenharmony_ci // continuous free space 25706f6ba60Sopenharmony_ci if (memcpy_s(buffer_ + tail_, bufSize_ - tail_, str.c_str(), len) != EOK) { 25806f6ba60Sopenharmony_ci return -1; 25906f6ba60Sopenharmony_ci } 26006f6ba60Sopenharmony_ci } else { 26106f6ba60Sopenharmony_ci // splitted free space 26206f6ba60Sopenharmony_ci if (memcpy_s(buffer_ + tail_, bufSize_ - tail_, str.c_str(), bufSize_ - tail_) != EOK) { 26306f6ba60Sopenharmony_ci return -1; 26406f6ba60Sopenharmony_ci } 26506f6ba60Sopenharmony_ci if (memcpy_s(buffer_, bufSize_, str.c_str() + bufSize_ - tail_, len + tail_ - bufSize_) != EOK) { 26606f6ba60Sopenharmony_ci return -1; 26706f6ba60Sopenharmony_ci } 26806f6ba60Sopenharmony_ci } 26906f6ba60Sopenharmony_ci // update buffer status 27006f6ba60Sopenharmony_ci tail_ += len; 27106f6ba60Sopenharmony_ci while (tail_ >= bufSize_) { 27206f6ba60Sopenharmony_ci tail_ -= bufSize_; 27306f6ba60Sopenharmony_ci } 27406f6ba60Sopenharmony_ci return len; 27506f6ba60Sopenharmony_ci} 27606f6ba60Sopenharmony_ci 27706f6ba60Sopenharmony_cichar* RingBuffer::Allocate(std::size_t bufSize) 27806f6ba60Sopenharmony_ci{ 27906f6ba60Sopenharmony_ci char *newBuffer {nullptr}; 28006f6ba60Sopenharmony_ci switch (alignShift_) { 28106f6ba60Sopenharmony_ci case B_ALIGN_SHIFT: { 28206f6ba60Sopenharmony_ci bufSize = (bufSize >> B_ALIGN_SHIFT); 28306f6ba60Sopenharmony_ci newBuffer = new(std::nothrow) char[bufSize]; 28406f6ba60Sopenharmony_ci break; 28506f6ba60Sopenharmony_ci } 28606f6ba60Sopenharmony_ci case H_ALIGN_SHIFT: { 28706f6ba60Sopenharmony_ci bufSize = (bufSize >> H_ALIGN_SHIFT); 28806f6ba60Sopenharmony_ci uint16_t *temp = new(std::nothrow) uint16_t[bufSize]; 28906f6ba60Sopenharmony_ci if (temp != nullptr) { 29006f6ba60Sopenharmony_ci newBuffer = reinterpret_cast<char *>(temp); 29106f6ba60Sopenharmony_ci } 29206f6ba60Sopenharmony_ci break; 29306f6ba60Sopenharmony_ci } 29406f6ba60Sopenharmony_ci case W_ALIGN_SHIFT: { 29506f6ba60Sopenharmony_ci bufSize = (bufSize >> W_ALIGN_SHIFT); 29606f6ba60Sopenharmony_ci uint32_t *temp = new(std::nothrow) uint32_t[bufSize]; 29706f6ba60Sopenharmony_ci if (temp != nullptr) { 29806f6ba60Sopenharmony_ci newBuffer = reinterpret_cast<char *>(temp); 29906f6ba60Sopenharmony_ci } 30006f6ba60Sopenharmony_ci break; 30106f6ba60Sopenharmony_ci } 30206f6ba60Sopenharmony_ci case D_ALIGN_SHIFT: { 30306f6ba60Sopenharmony_ci bufSize = (bufSize >> D_ALIGN_SHIFT); 30406f6ba60Sopenharmony_ci uint64_t *temp = new(std::nothrow) uint64_t[bufSize]; 30506f6ba60Sopenharmony_ci if (temp != nullptr) { 30606f6ba60Sopenharmony_ci newBuffer = reinterpret_cast<char *>(temp); 30706f6ba60Sopenharmony_ci } 30806f6ba60Sopenharmony_ci break; 30906f6ba60Sopenharmony_ci } 31006f6ba60Sopenharmony_ci } 31106f6ba60Sopenharmony_ci return newBuffer; 31206f6ba60Sopenharmony_ci} 31306f6ba60Sopenharmony_ci 31406f6ba60Sopenharmony_ciint RingBuffer::Resize() 31506f6ba60Sopenharmony_ci{ 31606f6ba60Sopenharmony_ci std::size_t expandedSize {bufSize_ << 1}; 31706f6ba60Sopenharmony_ci char* newBuf = Allocate(expandedSize); 31806f6ba60Sopenharmony_ci if (newBuf == nullptr) { 31906f6ba60Sopenharmony_ci return -1; 32006f6ba60Sopenharmony_ci } 32106f6ba60Sopenharmony_ci if (buffer_ == nullptr) { 32206f6ba60Sopenharmony_ci return -1; 32306f6ba60Sopenharmony_ci } 32406f6ba60Sopenharmony_ci // copy data to the new buffer 32506f6ba60Sopenharmony_ci auto dataSize = DataSize(); 32606f6ba60Sopenharmony_ci if (head_ + dataSize > bufSize_) { 32706f6ba60Sopenharmony_ci // data splitted 32806f6ba60Sopenharmony_ci if (memcpy_s(newBuf, expandedSize, buffer_ + head_, bufSize_ - head_) != EOK) { 32906f6ba60Sopenharmony_ci delete[] newBuf; 33006f6ba60Sopenharmony_ci return -1; 33106f6ba60Sopenharmony_ci } 33206f6ba60Sopenharmony_ci if (memcpy_s(newBuf + bufSize_ - head_, 33306f6ba60Sopenharmony_ci expandedSize - (bufSize_ - head_), 33406f6ba60Sopenharmony_ci buffer_, 33506f6ba60Sopenharmony_ci dataSize - (bufSize_ - head_)) != EOK) { 33606f6ba60Sopenharmony_ci delete[] newBuf; 33706f6ba60Sopenharmony_ci return -1; 33806f6ba60Sopenharmony_ci } 33906f6ba60Sopenharmony_ci } else { 34006f6ba60Sopenharmony_ci // continuous data 34106f6ba60Sopenharmony_ci if (memcpy_s(newBuf, expandedSize, buffer_ + head_, dataSize) != EOK) { 34206f6ba60Sopenharmony_ci delete[] newBuf; 34306f6ba60Sopenharmony_ci return -1; 34406f6ba60Sopenharmony_ci } 34506f6ba60Sopenharmony_ci } 34606f6ba60Sopenharmony_ci // update buffer status 34706f6ba60Sopenharmony_ci delete[] buffer_; 34806f6ba60Sopenharmony_ci buffer_ = newBuf; 34906f6ba60Sopenharmony_ci bufSize_ = expandedSize; 35006f6ba60Sopenharmony_ci head_ = 0; 35106f6ba60Sopenharmony_ci tail_ = dataSize; 35206f6ba60Sopenharmony_ci 35306f6ba60Sopenharmony_ci return 0; 35406f6ba60Sopenharmony_ci}