dont copy strings into textnodes
diff --git a/include/inja/node.hpp b/include/inja/node.hpp
index 6247d12..20435ac 100644
--- a/include/inja/node.hpp
+++ b/include/inja/node.hpp
@@ -74,9 +74,9 @@
 
 class TextNode : public AstNode {
 public:
-  std::string content;
+  size_t length;
 
-  explicit TextNode(nonstd::string_view content, size_t pos): AstNode(pos), content(content) { }
+  explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { }
 
   void accept(NodeVisitor& v) const {
     v.visit(*this);
@@ -108,14 +108,19 @@
   std::string name;
   std::string ptr {""};
 
-  explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name) {
-    // Convert dot notation to json pointer notation
+  static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) {
+    std::string result;
     do {
       nonstd::string_view part;
       std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
-      ptr.push_back('/');
-      ptr.append(part.begin(), part.end());
+      result.push_back('/');
+      result.append(part.begin(), part.end());
     } while (!ptr_name.empty());
+    return result;
+  }
+
+  explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name) {
+    ptr = convert_dot_to_json_ptr(ptr_name);
   }
 
   void accept(NodeVisitor& v) const {
diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp
index c6d46a3..909fd6e 100644
--- a/include/inja/parser.hpp
+++ b/include/inja/parser.hpp
@@ -471,7 +471,7 @@
         }
       } return;
       case Token::Kind::Text: {
-        current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text, tok.text.data() - tmpl.content.c_str()));
+        current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
       } break;
       case Token::Kind::StatementOpen: {
         get_next_token();
diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp
index 1c0d97e..08f822f 100644
--- a/include/inja/renderer.hpp
+++ b/include/inja/renderer.hpp
@@ -147,7 +147,7 @@
   }
 
   void visit(const TextNode& node) {
-    *output_stream << node.content;
+    output_stream->write(current_template->content.c_str() + node.pos, node.length);
   }
 
   void visit(const ExpressionNode&) { }
@@ -344,7 +344,7 @@
     } break;
     case Op::Exists: {
       auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
-      result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode(name, 0).ptr)));
+      result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
       json_tmp_stack.push_back(result_ptr);
       json_eval_stack.push(result_ptr.get());
     } break;
diff --git a/test/test-functions.cpp b/test/test-functions.cpp
index def0c6d..e42ede3 100644
--- a/test/test-functions.cpp
+++ b/test/test-functions.cpp
@@ -74,6 +74,7 @@
   SUBCASE("at") {
     CHECK(env.render("{{ at(names, 0) }}", data) == "Jeff");
     CHECK(env.render("{{ at(names, i) }}", data) == "Seb");
+    // CHECK(env.render("{{ at(names, 45) }}", data) == "Jeff");
   }
 
   SUBCASE("first") {