1+ #include <kclib/string.h>
2+ #include <kernel/error.h>
3+ #include <liballoc/liballoc.h>
4+ #include <utils/charqueue.h>
5+ #include <utils/spinlock.h>
6+
7+ constexpr unsigned int max_offset = PAGE_SIZE - sizeof (charqueue_page_t * ) - 1 ;
8+
9+ /*!
10+ * Check if queue is empty. A queue is empty if it is not initialised, initialised but has no pages
11+ * allocated, or has pages allocated but pointers are identical.
12+ * @param queue queue to check
13+ * @return whether queue is empty
14+ */
15+ static inline bool is_empty_charqueue (charqueue * queue ) {
16+ return queue == nullptr || queue -> head .current_page == nullptr ||
17+ (queue -> head .current_page == queue -> tail .current_page &&
18+ queue -> head .offset == queue -> tail .offset );
19+ }
20+
21+ /*!
22+ * Create a blank charqueue.
23+ * @return pointer to charqueue, or nullptr if couldn't allocate memory
24+ */
25+ charqueue * create_charqueue (void ) {
26+ charqueue * new_charqueue = kmalloc (sizeof (charqueue ));
27+ if (!new_charqueue ) return nullptr ;
28+ kmemset_explicit ((void * )new_charqueue , 0 , sizeof (charqueue ));
29+ return new_charqueue ;
30+ }
31+
32+ /*!
33+ * Push a single character to a charqueue. Behavior undefined if any input parameter or queue state
34+ * is invalid
35+ * @param queue valid charqueue
36+ * @param insert unsigned char to insert
37+ * @return 0 if successful, else error code
38+ */
39+ int push_charqueue (charqueue * queue , unsigned char insert ) {
40+ charqueue_page_t * new_page = nullptr ;
41+ if (queue -> tail .current_page == nullptr || queue -> tail .offset > max_offset ) {
42+ new_page = alloc_vpage (false);
43+ if (new_page == nullptr ) return - ENOMEM ;
44+ new_page -> next = nullptr ;
45+ }
46+
47+ uint64_t flags = spinlock_acquire (& queue -> lock );
48+ if (queue -> tail .current_page == nullptr || queue -> tail .offset > max_offset ) {
49+ if (new_page == nullptr ) {
50+ // TODO: this should be replaced with a quicker allocation function when available
51+ new_page = alloc_vpage (false);
52+ if (new_page == nullptr ) {
53+ spinlock_release (& queue -> lock , flags );
54+ return - ENOMEM ;
55+ }
56+ new_page -> next = nullptr ;
57+ }
58+ queue -> tail .offset = 0 ;
59+ if (queue -> tail .current_page == nullptr ) {
60+ queue -> head .current_page = queue -> tail .current_page = new_page ;
61+ queue -> head .offset = 0 ;
62+ } else {
63+ queue -> tail .current_page = queue -> tail .current_page -> next = new_page ;
64+ }
65+ new_page = nullptr ;
66+ }
67+
68+ queue -> tail .current_page -> data [queue -> tail .offset ++ ] = insert ;
69+ spinlock_release (& queue -> lock , flags );
70+
71+ if (new_page ) free_vpage (new_page );
72+ return 0 ;
73+ }
74+
75+ /*!
76+ * Pop a single character from a charqueue. Behavior undefined if any input parameter or queue state
77+ * is invalid
78+ * @param queue valid charqueue
79+ * @param ret pointer to unsigned char where value will be stored if successful
80+ * @return 0 if successful, else error code
81+ */
82+ int pop_charqueue (charqueue * queue , unsigned char * ret ) {
83+ uint64_t flags = spinlock_acquire (& queue -> lock );
84+ if (is_empty_charqueue (queue )) {
85+ spinlock_release (& queue -> lock , flags );
86+ return - EEMPQ ;
87+ }
88+ * ret = queue -> head .current_page -> data [queue -> head .offset ++ ];
89+ charqueue_page_t * page_to_free = nullptr ;
90+ if (queue -> head .offset > max_offset ) {
91+ // possible if current page is cleared out; then just reuse the same page
92+ if (queue -> head .current_page == queue -> tail .current_page ) {
93+ queue -> head .offset = queue -> tail .offset = 0 ;
94+ spinlock_release (& queue -> lock , flags );
95+ return 0 ;
96+ }
97+
98+ charqueue_page_t * next_page = queue -> head .current_page -> next ;
99+ page_to_free = queue -> head .current_page ;
100+ queue -> head .current_page = next_page ;
101+ queue -> head .offset = 0 ;
102+ }
103+
104+ spinlock_release (& queue -> lock , flags );
105+ if (page_to_free ) free_vpage (page_to_free );
106+ return 0 ;
107+ }
108+
109+ /*!
110+ * Peek a single character from a charqueue. Behavior undefined if any input parameter or queue
111+ * state is invalid
112+ * @param queue valid charqueue
113+ * @param ret pointer to unsigned char where value will be stored if successful
114+ * @return 0 if successful, else error code
115+ */
116+ int peek_charqueue (charqueue * queue , unsigned char * ret ) {
117+ uint64_t flags = spinlock_acquire (& queue -> lock );
118+ if (is_empty_charqueue (queue )) {
119+ spinlock_release (& queue -> lock , flags );
120+ return - EEMPQ ;
121+ }
122+ * ret = queue -> head .current_page -> data [queue -> head .offset ];
123+ spinlock_release (& queue -> lock , flags );
124+ return 0 ;
125+ }
126+
127+ /*!
128+ * Free a charqueue entirely. Behavior undefined if any input parameter or queue state is invalid.
129+ * Behavior undefined for other threads or processes that access the same queue while this function
130+ * is executing or after it has executed.
131+ * @param queue valid charqueue
132+ * @return 0 if successful, else error code
133+ */
134+ int free_charqueue (charqueue * queue ) {
135+ uint64_t flags = spinlock_acquire (& queue -> lock );
136+ charqueue_page_t * current_page = queue -> head .current_page ;
137+ queue -> head .current_page = queue -> tail .current_page = nullptr ;
138+ spinlock_release (& queue -> lock , flags );
139+
140+ while (current_page != nullptr ) {
141+ charqueue_page_t * page_stat = current_page ;
142+ current_page = current_page -> next ;
143+ free_vpage (page_stat );
144+ }
145+
146+ kfree (queue );
147+ return 0 ;
148+ }
0 commit comments