diff --git a/lib/incredis b/lib/incredis
index 0aee978..d2fff64 160000
--- a/lib/incredis
+++ b/lib/incredis
@@ -1 +1 @@
-Subproject commit 0aee978661e0f31722a28ff039490a98c829644b
+Subproject commit d2fff64381402a4ada9bbe3673603bbc81b2d0f6
diff --git a/src/kamokan_impl/CMakeLists.txt b/src/kamokan_impl/CMakeLists.txt
index 0fc0df1..adc743b 100644
--- a/src/kamokan_impl/CMakeLists.txt
+++ b/src/kamokan_impl/CMakeLists.txt
@@ -22,6 +22,7 @@ add_library(${PROJECT_NAME} STATIC
string_conv.cpp
edit_response.cpp
general_pastie_response.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/include/lua_scripts_for_redis.cpp
)
target_include_directories(${PROJECT_NAME}
@@ -62,3 +63,10 @@ configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/kamokan_config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/kamokan_config.h"
)
+
+add_custom_command(
+ OUTPUT include/lua_scripts_for_redis.cpp
+ COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" -P ${CMAKE_CURRENT_SOURCE_DIR}/lua_to_cpp.cmake
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/retrieve_pastie.lua ${CMAKE_CURRENT_SOURCE_DIR}/save_pastie.lua
+ COMMENT "Embedding save/load lua scripts into the c++ code"
+)
diff --git a/src/kamokan_impl/lua_scripts_for_redis.hpp b/src/kamokan_impl/lua_scripts_for_redis.hpp
new file mode 100644
index 0000000..c2c2cd3
--- /dev/null
+++ b/src/kamokan_impl/lua_scripts_for_redis.hpp
@@ -0,0 +1,27 @@
+/* Copyright 2017, Michele Santullo
+ * This file is part of "kamokan".
+ *
+ * "kamokan" is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * "kamokan" is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with "kamokan". If not, see .
+ */
+
+#pragma once
+
+#include
+
+namespace kamokan {
+ extern const char g_save_script[];
+ extern const char g_load_script[];
+ extern const std::size_t g_save_script_size;
+ extern const std::size_t g_load_script_size;
+} //namespace kamokan
diff --git a/src/kamokan_impl/lua_to_cpp.cmake b/src/kamokan_impl/lua_to_cpp.cmake
new file mode 100644
index 0000000..18987ec
--- /dev/null
+++ b/src/kamokan_impl/lua_to_cpp.cmake
@@ -0,0 +1,31 @@
+file(READ ${SOURCE_DIR}/retrieve_pastie.lua retrieve_pastie)
+file(READ ${SOURCE_DIR}/save_pastie.lua save_pastie)
+string(REGEX REPLACE "[ \t]*=[ \t]*" "=" retrieve_pastie "${retrieve_pastie}")
+string(REGEX REPLACE "[ \t]*=[ \t]*" "=" save_pastie "${save_pastie}")
+string(REGEX REPLACE ",[ \t]*" "," retrieve_pastie "${retrieve_pastie}")
+string(REGEX REPLACE ",[ \t]*" "," save_pastie "${save_pastie}")
+string(REGEX REPLACE "(^|\n)[ \t]+" "\\1" retrieve_pastie "${retrieve_pastie}")
+string(REGEX REPLACE "(^|\n)[ \t]+" "\\1" save_pastie "${save_pastie}")
+string(LENGTH "${retrieve_pastie}" retrieve_pastie_length)
+string(LENGTH "${save_pastie}" save_pastie_length)
+
+set(lua_scripts_for_redis_content "//File autogenerated by cmake, changes will be lost
+#include
+namespace kamokan {
+extern const char g_save_script[] = R\"lua(${save_pastie})lua\";
+extern const char g_load_script[] = R\"lua(${retrieve_pastie})lua\";
+extern const std::size_t g_save_script_size = ${save_pastie_length} + 1;
+extern const std::size_t g_load_script_size = ${retrieve_pastie_length} + 1;
+} //namespace kamokan
+")
+
+file(WRITE
+ include/lua_scripts_for_redis.cpp
+ "${lua_scripts_for_redis_content}"
+)
+
+unset(lua_scripts_for_redis_content)
+unset(save_pastie_length)
+unset(retrieve_pastie_length)
+unset(save_pastie)
+unset(retrieve_pastie)
diff --git a/src/kamokan_impl/retrieve_pastie.lua b/src/kamokan_impl/retrieve_pastie.lua
new file mode 100644
index 0000000..de9ff4f
--- /dev/null
+++ b/src/kamokan_impl/retrieve_pastie.lua
@@ -0,0 +1,10 @@
+local token = KEYS[1]
+local result = redis.call("HMGET", token, "pastie", "selfdes", "lang")
+local selfdes = 0
+local deleted = 0
+if result[2] == 1 then
+ deleted = redis.call("DEL", token)
+ selfdes = 1
+end
+
+return {result[1], selfdes, deleted, result[3]}
diff --git a/src/kamokan_impl/save_pastie.lua b/src/kamokan_impl/save_pastie.lua
new file mode 100644
index 0000000..3dc39c9
--- /dev/null
+++ b/src/kamokan_impl/save_pastie.lua
@@ -0,0 +1,29 @@
+function num_to_value (num)
+ local retval = ""
+ local running = true
+
+ while (running)
+ do
+ local remainder = num % 26
+ retval = string.char(97 + remainder) .. retval
+ num = math.floor(num / 26)
+ running = (num ~= 0)
+ end
+ return retval
+end
+
+paste_counter = KEYS[1]
+next_id = redis.call("INCR", paste_counter) - 1
+token = num_to_token(next_id)
+text = ARGV[1]
+ttl = ARGV[2]
+lang = ARGV[3]
+selfdestruct = ARGV[4]
+redis.call("HMSET", token,
+ "pastie", text,
+ "max_ttl", ttl,
+ "lang", lang,
+ "selfdes", selfdestruct
+)
+
+return token
diff --git a/src/kamokan_impl/storage.cpp b/src/kamokan_impl/storage.cpp
index 9193b77..19bf212 100644
--- a/src/kamokan_impl/storage.cpp
+++ b/src/kamokan_impl/storage.cpp
@@ -23,6 +23,7 @@
#include "spdlog.hpp"
#include "truncated_string.hpp"
#include "string_conv.hpp"
+#include "lua_scripts_for_redis.hpp"
#include
#include
#include
@@ -129,6 +130,7 @@ namespace kamokan {
const std::string& parRemoteIP
) const {
using tawashi::ErrorReasons;
+ using redis::RedisInt;
if (not is_connected())
return make_submission_result(ErrorReasons::RedisDisconnected);
@@ -156,7 +158,7 @@ namespace kamokan {
"pastie", parText,
"max_ttl", dhandy::lexical_cast(parExpiry),
"lang", parLang,
- "selfdes", (parSelfDestruct ? "1" : "0"))
+ "selfdes", static_cast(parSelfDestruct ? 1 : 0))
) {
redis.set(parRemoteIP, "");
redis.expire(parRemoteIP, m_settings->as("resubmit_wait"));
@@ -168,7 +170,6 @@ namespace kamokan {
}
auto Storage::retrieve_pastie (const boost::string_view& parToken, uint32_t parMaxTokenLen) const -> RetrievedPastie {
- using opt_string = redis::IncRedis::opt_string;
using opt_string_list = redis::IncRedis::opt_string_list;
RetrievedPastie retval;
@@ -176,18 +177,26 @@ namespace kamokan {
if (not retval.valid_token)
return retval;
- opt_string_list pastie_reply = m_redis->hmget(parToken, "pastie", "selfdes", "lang");
- retval.pastie = (pastie_reply and not pastie_reply->empty() ? (*pastie_reply)[0] : opt_string());
- opt_string selfdes = (pastie_reply and pastie_reply->size() > 1 ? (*pastie_reply)[1] : opt_string());
- retval.lang = (pastie_reply and pastie_reply->size() > 2 ? (*pastie_reply)[2] : opt_string());
- if (selfdes and string_conv(*selfdes)) {
- const bool deleted = m_redis->del(parToken);
- retval.self_destructed = deleted;
- if (not deleted) {
- auto statuslog = spdlog::get("statuslog");
- statuslog->error("Pastie \"{}\" was marked as self-destructing but DEL failed to delete it", parToken);
- }
+ redis::Script retrieve = m_redis->command().make_script(boost::string_view(g_load_script, g_load_script_size - 1));
+ auto batch = m_redis->command().make_batch();
+ retrieve.run(batch, std::make_tuple(parToken), std::make_tuple());
+ auto raw_replies = batch.replies();
+ if (raw_replies.empty())
+ return retval;
+
+ assert(not raw_replies.front().is_error());
+ auto pastie_reply = get_array(raw_replies.front());
+
+ retval.pastie = get_string(pastie_reply[0]);
+ const redis::RedisInt selfdes = get_integer(pastie_reply[1]);
+ const redis::RedisInt deleted = get_integer(pastie_reply[2]);
+ retval.lang = get_string(pastie_reply[3]);
+ retval.self_destructed = selfdes and deleted;
+
+ if (selfdes and not deleted) {
+ auto statuslog = spdlog::get("statuslog");
+ statuslog->error("Pastie \"{}\" was marked as self-destructing but DEL failed to delete it", parToken);
}
#if defined(SPDLOG_DEBUG_ON)