databaseRead.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package lib
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "sort"
  6. )
  7. //topicPointer and textPointer should not be exported
  8. //mb move tweet reconstruction to dbRead from servers
  9. type Tweet struct {
  10. TopicPointer string
  11. TextPointer int
  12. Topics []string
  13. Text string
  14. RoundPosted int
  15. }
  16. var dbR = make(map[string][]Tweet)
  17. var archive = make(map[string][]Tweet)
  18. //has to be dividable by 32
  19. var minimumBlockSize int
  20. //needs to be dividable by roundsBeforUpdate
  21. var roundsBeforeArchiving = 12
  22. var roundsBeforeArchivingInc = roundsBeforeArchiving
  23. var bytesSaved float64
  24. var topicList []string
  25. var archiveTopicList []string
  26. func NewEntries(inputTweets []Tweet, whereTo int) {
  27. tmpdb := dbR
  28. if whereTo == 1 {
  29. tmpdb = archive
  30. }
  31. var position int = 0
  32. for _, tweet := range inputTweets {
  33. for index := range tweet.Topics {
  34. //fmt.Println("topic to put in", tweet.Topics[index])
  35. //new topic
  36. if _, ok := tmpdb[tweet.Topics[index]]; !ok {
  37. if whereTo == 0 {
  38. topicList = append(topicList, tweet.Topics[index])
  39. } else {
  40. archiveTopicList = append(archiveTopicList, tweet.Topics[index])
  41. }
  42. }
  43. //new tweet
  44. if index == 0 {
  45. position = len(tmpdb[tweet.Topics[0]])
  46. tmpdb[tweet.Topics[index]] = append(tmpdb[tweet.Topics[0]], tweet)
  47. } else {
  48. //known tweet
  49. //setting pointer for all other Topics
  50. if whereTo == 1 {
  51. bytesSaved += float64(len(tweet.Topics)) + float64(len(tweet.Text))
  52. }
  53. topic := tweet.Topics[index]
  54. var pointerTweet Tweet
  55. pointerTweet.TopicPointer = tweet.Topics[0]
  56. pointerTweet.TextPointer = position
  57. pointerTweet.Topics = nil
  58. pointerTweet.Text = ""
  59. tmpdb[topic] = append(tmpdb[topic], pointerTweet)
  60. }
  61. }
  62. }
  63. if whereTo == 0 {
  64. dbR = tmpdb
  65. } else {
  66. archive = tmpdb
  67. }
  68. }
  69. //todo! add round to pirquery only get tweets that have been posted from that round onward
  70. func GetTweets(pirQuery []byte, dataLength int, whereFrom int, pubKey [32]byte) []byte {
  71. //fmt.Println("query", pirQuery)
  72. //fmt.Println("dbR", dbR)
  73. tmpdb := dbR
  74. if whereFrom == 1 {
  75. tmpdb = archive
  76. }
  77. //50 is an estimated nr that works, there probably is a smarter way to do this
  78. minimumBlockSize = 50 * maxTweetAmount(whereFrom)
  79. var wantedTopics = getNamesForTopics(pirQuery, whereFrom)
  80. tweetsToReturn := make([][]Tweet, len(wantedTopics))
  81. for index, wantedTopic := range wantedTopics {
  82. for _, tweet := range tmpdb[wantedTopic] {
  83. //fmt.Println(tweet)
  84. //new Tweet
  85. if tweet.Text != "" {
  86. tweet.RoundPosted = 0
  87. tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
  88. } else {
  89. //"copied" tweet
  90. //find tweet with pointers
  91. tweet = tmpdb[tweet.TopicPointer][tweet.TextPointer]
  92. tweet.RoundPosted = 0
  93. tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
  94. }
  95. }
  96. }
  97. return tweetsToByteArray(tweetsToReturn, whereFrom, wantedTopics)
  98. }
  99. func maxTweetAmount(whereFrom int) int {
  100. tmpdb := dbR
  101. if whereFrom == 1 {
  102. tmpdb = archive
  103. }
  104. var max int = 0
  105. for i := range tmpdb {
  106. nrOfTweets := len(tmpdb[i])
  107. if nrOfTweets > max {
  108. max = nrOfTweets
  109. }
  110. }
  111. return max
  112. }
  113. func getNamesForTopics(wantedIndices []byte, whereFrom int) []string {
  114. var topicNames []string
  115. tmpTopicList := topicList
  116. if whereFrom == 1 {
  117. tmpTopicList = archiveTopicList
  118. }
  119. for index, element := range wantedIndices {
  120. if index == len(tmpTopicList) {
  121. break
  122. }
  123. if element == 1 {
  124. topicNames = append(topicNames, tmpTopicList[index])
  125. }
  126. }
  127. return topicNames
  128. }
  129. //transform struct to byte array for sending
  130. func tweetsToByteArray(tweetsToReturn [][]Tweet, whereFrom int, wantedTopics []string) []byte {
  131. tweetsAsBytes := make([]byte, minimumBlockSize)
  132. for _, block := range tweetsToReturn {
  133. var topicPadding []string
  134. var blockToAppend []byte
  135. for index, tweet := range block {
  136. for topicIndex, topic := range tweet.Topics {
  137. blockToAppend = append(blockToAppend, []byte(topic)...)
  138. blockToAppend = append(blockToAppend, ","...)
  139. //gets the topic used for padding
  140. if topicIndex > 0 && index < len(wantedTopics) && topic != wantedTopics[index] {
  141. topicPadding = append(topicPadding, topic)
  142. }
  143. }
  144. //replaces last "," with ";;" bc there is text following and not another topic
  145. blockToAppend = blockToAppend[:len(blockToAppend)-1]
  146. blockToAppend = append(blockToAppend, []byte(";;")[:]...)
  147. blockToAppend = append(blockToAppend, []byte(tweet.Text)...)
  148. blockToAppend = append(blockToAppend, []byte(";")[0])
  149. }
  150. //adds padding
  151. blockToAppend = append(blockToAppend, []byte(";;;")[:]...)
  152. remainingLength := minimumBlockSize - len(blockToAppend)
  153. //grouping using topic from recovered tweets
  154. index := len(topicPadding) - 1
  155. for index >= 0 && remainingLength > 0 && topicPadding[index] != "" {
  156. paddingTweet, err := getNextTweet(topicPadding[index], index, whereFrom)
  157. if err {
  158. break
  159. }
  160. if remainingLength < len(paddingTweet) {
  161. break
  162. }
  163. blockToAppend = append(blockToAppend, paddingTweet...)
  164. remainingLength -= len(paddingTweet)
  165. index--
  166. }
  167. padding := bytes.Repeat([]byte(";"), remainingLength)
  168. blockToAppend = append(blockToAppend, padding...)
  169. Xor(blockToAppend, tweetsAsBytes)
  170. //fmt.Println(tweetsAsBytes)
  171. }
  172. //fmt.Println("length Returned", len(tweetsAsBytes))
  173. return tweetsAsBytes
  174. }
  175. //gets the Tweet at index from wantedTopic for padding
  176. func getNextTweet(wantedTopic string, index, whereFrom int) ([]byte, bool) {
  177. tmpdb := dbR
  178. if whereFrom == 1 {
  179. tmpdb = archive
  180. }
  181. var tweetToReturn Tweet
  182. for tweetIndex, tweet := range tmpdb[wantedTopic] {
  183. if len(tmpdb[wantedTopic]) <= index {
  184. return nil, true
  185. } else if tweetIndex < index {
  186. continue
  187. } else if tweetIndex > index {
  188. break
  189. }
  190. //new Tweet
  191. if tweet.Text != "" {
  192. tweet.RoundPosted = 0
  193. tweetToReturn = tweet
  194. } else {
  195. //"copied" tweet
  196. //find tweet with pointers
  197. tweet = tmpdb[tweet.TopicPointer][tweet.TextPointer]
  198. tweet.RoundPosted = 0
  199. tweetToReturn = tweet
  200. }
  201. }
  202. //fmt.Println("nextTweet", tweetToReturn)
  203. var tweetToReturnBytes []byte
  204. for _, topic := range tweetToReturn.Topics {
  205. tweetToReturnBytes = append(tweetToReturnBytes, []byte(topic)...)
  206. tweetToReturnBytes = append(tweetToReturnBytes, ","...)
  207. }
  208. //replaces last "," with ";;" bc there is text following and not another topic
  209. tweetToReturnBytes = tweetToReturnBytes[:len(tweetToReturnBytes)-1]
  210. tweetToReturnBytes = append(tweetToReturnBytes, []byte(";;")[:]...)
  211. tweetToReturnBytes = append(tweetToReturnBytes, []byte(tweetToReturn.Text)...)
  212. tweetToReturnBytes = append(tweetToReturnBytes, []byte(";")[0])
  213. return tweetToReturnBytes, false
  214. }
  215. //see func name
  216. func GetTopicList(whereFrom int) ([]byte, int) {
  217. tmpTopicList := topicList
  218. if whereFrom == 1 {
  219. tmpTopicList = archiveTopicList
  220. }
  221. if (len(tmpTopicList)) == 0 {
  222. return nil, 0
  223. }
  224. topicByteArray := new(bytes.Buffer)
  225. json.NewEncoder(topicByteArray).Encode(tmpTopicList)
  226. return topicByteArray.Bytes(), len(tmpTopicList)
  227. }
  228. //iterates through full dbR and moves old tweets to archive
  229. func CleanUpdbR(round int) {
  230. if roundsBeforeArchiving == -1 {
  231. return
  232. }
  233. if roundsBeforeArchivingInc-round == 0 {
  234. keys := make([]string, len(dbR))
  235. i := 0
  236. for k := range dbR {
  237. keys[i] = k
  238. i++
  239. }
  240. sort.Strings(keys)
  241. var tweetsToArchive []Tweet
  242. for _, topic := range keys {
  243. tweets := dbR[topic]
  244. for i := len(tweets) - 1; i >= 0; i-- {
  245. if round-roundsBeforeArchiving >= tweets[i].RoundPosted {
  246. //only adds the tweet to the archive when there is text
  247. if tweets[i].Text != "" {
  248. tweetsToArchive = append(tweetsToArchive, tweets[i])
  249. }
  250. //delets the tweet from the array
  251. row := append(tweets[:i], tweets[i+1:]...)
  252. tweets = row
  253. }
  254. }
  255. dbR[topic] = tweets
  256. }
  257. //fmt.Println("tweetsToArchive", len(tweetsToArchive))
  258. NewEntries(tweetsToArchive, 1)
  259. roundsBeforeArchivingInc += roundsBeforeArchiving
  260. //redoes the whole dbR to correct pointers
  261. var tweetsToMain []Tweet
  262. for _, topic := range keys {
  263. tweets := dbR[topic]
  264. for _, tweet := range tweets {
  265. if tweet.Text != "" {
  266. tweetsToMain = append(tweetsToMain, tweet)
  267. }
  268. }
  269. }
  270. dbR = nil
  271. dbR = make(map[string][]Tweet)
  272. topicList = nil
  273. NewEntries(tweetsToMain, 0)
  274. }
  275. }
  276. func GetBytesSaved() float64 {
  277. return bytesSaved / float64(len(archive))
  278. }