diff --git a/2017/03/06/2017-3-6-grails-call-java/index.html b/2017/03/06/2017-3-6-grails-call-java/index.html index 9fbcf7e1..3b362716 100644 --- a/2017/03/06/2017-3-6-grails-call-java/index.html +++ b/2017/03/06/2017-3-6-grails-call-java/index.html @@ -27,8 +27,8 @@ - + @@ -360,10 +360,10 @@

#java - #grails - #gradle + #grails + diff --git a/en/2024/07/18/2024-7-18-slang-ast-hier-tree/index.html b/en/2024/07/18/2024-7-18-slang-ast-hier-tree/index.html index dd4ad807..8d5df7cf 100644 --- a/en/2024/07/18/2024-7-18-slang-ast-hier-tree/index.html +++ b/en/2024/07/18/2024-7-18-slang-ast-hier-tree/index.html @@ -19,7 +19,7 @@ - + @@ -407,7 +407,7 @@

Summa
How to Better Print Syntax Tree Structures
-
https://threelambda.com/2024/07/18/2024-7-18-slang-ast-hier-tree/
+
https://threelambda.com/en/2024/07/18/2024-7-18-slang-ast-hier-tree/
diff --git a/en/404.html b/en/404.html index aaa60576..588115ee 100644 --- a/en/404.html +++ b/en/404.html @@ -18,7 +18,7 @@ - + diff --git a/en/about/index.html b/en/about/index.html index f0dd3617..f56b88c7 100644 --- a/en/about/index.html +++ b/en/about/index.html @@ -19,7 +19,7 @@ - + diff --git a/en/archives/2024/07/index.html b/en/archives/2024/07/index.html index 4675c99c..00ef1982 100644 --- a/en/archives/2024/07/index.html +++ b/en/archives/2024/07/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/archives/2024/index.html b/en/archives/2024/index.html index c87bee64..e7f35abf 100644 --- a/en/archives/2024/index.html +++ b/en/archives/2024/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/archives/index.html b/en/archives/index.html index 1a2a6429..105a9598 100644 --- a/en/archives/index.html +++ b/en/archives/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/categories/index.html b/en/categories/index.html index 30f731e4..15a5c0b8 100644 --- a/en/categories/index.html +++ b/en/categories/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/index.html b/en/index.html index d6559501..c6aa27fc 100644 --- a/en/index.html +++ b/en/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/links/index.html b/en/links/index.html index 95830753..e4338e90 100644 --- a/en/links/index.html +++ b/en/links/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/sitemap.xml b/en/sitemap.xml index 40f71a5e..380c24b1 100644 --- a/en/sitemap.xml +++ b/en/sitemap.xml @@ -2,7 +2,7 @@ - https://threelambda.com/about/index.html + https://threelambda.com/en/about/index.html 2024-07-22 @@ -11,7 +11,7 @@ - https://threelambda.com/2024/07/18/2024-7-18-slang-ast-hier-tree/ + https://threelambda.com/en/2024/07/18/2024-7-18-slang-ast-hier-tree/ 2024-07-22 @@ -20,7 +20,7 @@ - https://threelambda.com/tags/index.html + https://threelambda.com/en/tags/index.html 2024-07-22 @@ -30,7 +30,7 @@ - https://threelambda.com/ + https://threelambda.com/en 2024-07-22 daily 1.0 @@ -38,21 +38,21 @@ - https://threelambda.com/tags/c/ + https://threelambda.com/en/tags/c/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/AST/ + https://threelambda.com/en/tags/AST/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/tree/ + https://threelambda.com/en/tags/tree/ 2024-07-22 weekly 0.2 diff --git a/en/tags/AST/index.html b/en/tags/AST/index.html index b7a6f817..6a90bb63 100644 --- a/en/tags/AST/index.html +++ b/en/tags/AST/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/tags/c/index.html b/en/tags/c/index.html index 97809933..e57d43a9 100644 --- a/en/tags/c/index.html +++ b/en/tags/c/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/tags/index.html b/en/tags/index.html index b7401144..e01e58f0 100644 --- a/en/tags/index.html +++ b/en/tags/index.html @@ -18,7 +18,7 @@ - + diff --git a/en/tags/tree/index.html b/en/tags/tree/index.html index b94bdabb..5d5da897 100644 --- a/en/tags/tree/index.html +++ b/en/tags/tree/index.html @@ -18,7 +18,7 @@ - + diff --git a/local-search.xml b/local-search.xml index 933e3533..cf203fd4 100644 --- a/local-search.xml +++ b/local-search.xml @@ -829,10 +829,10 @@ java - grails - gradle + grails + diff --git a/page/4/index.html b/page/4/index.html index 48e2588a..0ef88ae3 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -507,10 +507,10 @@

#java - #grails - #gradle + #grails +

diff --git a/search.xml b/search.xml index 7b9dc6c9..24cab16d 100644 --- a/search.xml +++ b/search.xml @@ -81,6 +81,45 @@ python默认的递归栈深度是10000。

python + + 使用pandas.read_csv()读取csv文件 + /2016/12/22/pandas-read-csv/ + 问题

以下Python代码实现对Excel转存的csv文件进行读取。

+
import pandas as pd
df = pd.read_csv(file_path + file_name + ".csv", encoding="gbk")
+ + + +

csv文件入库是一件脏活。表面上看csv文件是一个非常简单的 +逗号分隔符文件。但是其实不然。Excel转存的csv文件并不是标准的以逗号作为分隔符, +并且对所有的项用双引号包裹。现在我就遇到了从Oracle导出的csv文件,以上的代码 +不起作用了。

+

解决问题

究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()读取 +这种标准csv文件的方法。还是先把问题简化一下,看看Pythoncsv模块是如何读取的吧。 +简单的查找就可以找到答案。

+
import csv
csv.register_dialect(
'mydialect',
delimiter = ',',
quotechar = '"',
doublequote = True,
skipinitialspace = True,
lineterminator = '\r\n',
quoting = csv.QUOTE_MINIMAL)

print('\n Output from an iterable object created from the csv file')
with open('smallsample.csv', 'rb') as mycsvfile:
thedata = csv.reader(mycsvfile, dialect='mydialect')
for row in thedata:
print(row[0]+"\t \t"+row[1]+"\t \t"+row[4])
+ +

好的,pandas.read_csv()肯定是要调用csv模块的,那么看看 +它的方法参数表吧。

+
+

pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶

+
+

真是够长的,还是搜一下有没有dialect=,呵呵,果然有。

+
+

dialect : str or csv.Dialect instance, default None
+ If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details

+
+

那么问题解决了。把第一段代码改改吧。

+
import pandas as pd
df = pd.read_csv(file_path + file_name + ".csv", encoding="gbk", dialect='mydialect')
+ +

一运行还是报错,这是怎么回事呢,编码换成utf8,也不行。最后才发现 +需要使用gb18030才行。即使使用chardet的编码探测模块,也不一定能探测出来,因为整个文档 +只有少数字符是超出了gbk,所以不可能既高效又准确的解决这个问题。

+]]> + + python + 技巧 + + [leetcode 282]Expression Add Operators 原创解法 /2016/12/19/leetcode-282/ @@ -194,42 +233,163 @@ the (unevaluated) expression expr, substituting any variables bound in env.

- 使用pandas.read_csv()读取csv文件 - /2016/12/22/pandas-read-csv/ - 问题

以下Python代码实现对Excel转存的csv文件进行读取。

-
import pandas as pd
df = pd.read_csv(file_path + file_name + ".csv", encoding="gbk")
+ [leetcode 327]Count of Range Sum 原创解法 + /2016/12/31/leetcode-327/ + 题目概述

原题链接

+
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
+Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.
 
+Example:
+Given nums = [-2, 5, -1], lower = -2, upper = 2,
+Return 3.
+The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
+
-

csv文件入库是一件脏活。表面上看csv文件是一个非常简单的 -逗号分隔符文件。但是其实不然。Excel转存的csv文件并不是标准的以逗号作为分隔符, -并且对所有的项用双引号包裹。现在我就遇到了从Oracle导出的csv文件,以上的代码 -不起作用了。

-

解决问题

究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()读取 -这种标准csv文件的方法。还是先把问题简化一下,看看Pythoncsv模块是如何读取的吧。 -简单的查找就可以找到答案。

-
import csv
csv.register_dialect(
'mydialect',
delimiter = ',',
quotechar = '"',
doublequote = True,
skipinitialspace = True,
lineterminator = '\r\n',
quoting = csv.QUOTE_MINIMAL)

print('\n Output from an iterable object created from the csv file')
with open('smallsample.csv', 'rb') as mycsvfile:
thedata = csv.reader(mycsvfile, dialect='mydialect')
for row in thedata:
print(row[0]+"\t \t"+row[1]+"\t \t"+row[4])
+

O(N^2)解法

最直接的想法当然就是遍历求和了。但是这样是无法通过测试的。

+
class Solution3(object):
def countRangeSum(self, nums, lower, upper):
"""
:type nums: List[int]
:type lower: int
:type upper: int
:rtype: int
"""

sums = []

for i in range(len(nums)):
s = 0
for j in range(i, len(nums)):
s += nums[j]
if lower <= s <= upper:
sums.append(s)
return len(sums)

-

好的,pandas.read_csv()肯定是要调用csv模块的,那么看看 -它的方法参数表吧。

-
-

pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶

-
-

真是够长的,还是搜一下有没有dialect=,呵呵,果然有。

-
-

dialect : str or csv.Dialect instance, default None
- If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details

-
-

那么问题解决了。把第一段代码改改吧。

-
import pandas as pd
df = pd.read_csv(file_path + file_name + ".csv", encoding="gbk", dialect='mydialect')
+

死亡凝视

必须要想出复杂度是Nlog(N)的算法才行。但是问题也很明显, +要想找出所有的s(i,j) for 0 <= i <= j < n and lower <= s(i,j) <= upper +那必须要进行遍历啊,因此时间复杂度必须是N^2。 +啊啊,实在想不出来了。

+

第二天,既然要求时间复杂度为NlogN那么就必然不能遍历求出s(i,j),但是 +题目要求的是求出Num(s(i,j)) for 0 <= i <= j < n and lower <= s(i,j) <= upper +,也就是说给出个数就行,因此确实不一定要给出所有的s(i,j)。嗯,有点进展了。

+

第三天,排序算法的复杂度是NlogN,怎么才能依靠排序算法求出呢。难道对所有的 +s(i,j)进行排序么?这样肯定不行。 +能否使用老办法画图呢?这样好观察一下。但是仔细想了想,这是一个三维图。 +纸上画不出来。 +只好画一个二维图吧。下图是对数组[5,7,8,-2,1]画的二维表格。格子 +中的数字就是s(i,j)

+

327-1

+

我仔细的盯着看。我给这种解决问题的方式,起了 +个名字——死亡凝视。看过The Big Bang的朋友可能记得SheldonRaj一起 +工作的样子,就是一动不动的看着黑板上的公式,看上一整天。

+

327-2

+

嘿嘿,你们不要笑啊,我的方式差不多。只不过是盯着表格。这样的表格 +其实我画了很多。但是只有这个我看出了规律。

+

327-3

+

看出来了么,三列带颜色的数字。是不是发现它们有相同的趋势呢? +是的它们有相同的趋势。

+

如果没有看出来。我再画一个图。看看这三列数字和蓝色数字的关系。

+

327-4

+

是的。20 - 5 = 1518 - 5 = 1319 - 5 = 14。 +假如上下区间为[12,18],那么对于排序后的第一列[5,12,18,19,20], +可以使用二分查找法找出上限的位置是2,下限的位置为1,个数为2。 +那么第二列[7,15,13,14][12,18]的数字的个数为3。 +怎么根据第一列排序后的数字求出呢。嗯,很简单。 +我们把上下限加上5,变成17, 23,这样就可以发现[5,12,18,19,20]找出 +个数为3。嗯,哈哈。真是不错。这样子,就不需要求出所有的s(i,j)了。

+

但是,别着急,还是有问题,我们需要在计算第二列的个数的之前,把第一列的数字中的5排除掉。 +否则的话,如果区间是[-100,100],那么我们就多计算了。

+

通过的代码

# python3.5
class Solution4(object):
def countRangeSum(self, nums, lower, upper):
"""
思路:对 list0 = [s(0,j) for j in (0,n)]先进行排序。
list1 = [s(1,j) for j in (1,n)],可以由 list0生成。

:type nums: List[int]
:type lower: int
:type upper: int
:rtype: int
"""
if not nums:
return 0
else:
result = 0
n = len(nums)
l = []
l2 = []
sum = 0
for i in range(0, n):
sum += nums[i]
l.append((sum, i))
l2.append((sum, i))
l.sort()
for i in range(0, n):
if i > 0:
lower += nums[i - 1]
upper += nums[i - 1]
lower_pos = self.find_lower(l, lower)
upper_pos = self.find_uppper(l, upper)
if lower_pos is not None and upper_pos is not None:
result += upper_pos - lower_pos + 1
pos = self.find_lower(l, l2[i][0])
l.pop(pos) # <-- 这里是个接近O(N)的操作

return result

def find(self, nums, target):
n = len(nums)

start = 0
end = n
mid = n // 2
new_mid = None

while 0 <= mid < n:
if nums[mid][0] == target:
break
elif nums[mid][0] < target and mid + 1 < n and nums[mid + 1][0] > target:
break
elif nums[mid][0] < target:
start = mid
new_mid = (start + end) // 2
elif nums[mid][0] > target:
end = mid
new_mid = (start + end) // 2
if new_mid == mid:
mid += 1
else:
mid = new_mid

return mid if nums[mid][0] == target else mid + 1

def find_lower(self, nums, lower):
n = len(nums)
if lower > nums[n - 1][0]:
return None
elif lower < nums[0][0]:
return 0
else:
mid = self.find(nums, lower)
while mid - 1 >= 0 and nums[mid - 1][0] == lower:
mid -= 1
return mid

def find_uppper(self, nums, upper):
n = len(nums)
if upper > nums[n - 1][0]:
return n - 1
elif upper < nums[0][0]:
return None
else:
mid = self.find(nums, upper)
while mid < n - 1 and nums[mid + 1][0] == upper:
mid += 1
return mid if nums[mid][0] == upper else mid - 1
-

一运行还是报错,这是怎么回事呢,编码换成utf8,也不行。最后才发现 -需要使用gb18030才行。即使使用chardet的编码探测模块,也不一定能探测出来,因为整个文档 -只有少数字符是超出了gbk,所以不可能既高效又准确的解决这个问题。

+

总结

排序操作的时间复杂度是O(NlogN),而找出每列一列的个数的时间复杂度是O(logN) +因为一共有N列,所以时间复杂度为O(NlogN),另外 +我们在代码中使用了对List数据的非尾部的pop操作,这个操作的时间 +复杂度接近O(N),严格来说,我们的代码的时间复杂度还是O(N^2)。如果能够不使用这个操作。 +我们的代码速度会更快。

+
l.pop(pos) # <-- 这里是个接近O(N)的操作
+
]]>
+ leetcode + 算法 + python + +
+ + [leetcode 315]Count of Smaller Numbers After Self 原创解法 + /2016/12/26/leetcode-315/ + 题目概述

原题链接

+
You are given an integer array nums and you
+have to return a new counts array. 
+The counts array has the property where counts[i] is 
+the number of smaller elements to the right of nums[i].
+
+Example:
+
+Given nums = [5, 2, 6, 1]
+
+To the right of 5 there are 2 smaller elements (2 and 1).
+To the right of 2 there is only 1 smaller element (1).
+To the right of 6 there is 1 smaller element (1).
+To the right of 1 there is 0 smaller element.
+Return the array [2, 1, 1, 0].
+
+ + +

拿到这个题,最先想到的是时间复杂度O(N*N) +的解法。那就实现一下吧。

+
class Solution2(object):
def countSmaller(self, nums):
"""
O(N*N)
:type nums: List[int]
:rtype: List[int]
"""
if nums:
n = len(nums)
result = []
for i in range(n):
cur_val = nums[i]
smaller_n = 0
for j in range(i, n):
if nums[j] < cur_val:
smaller_n += 1

result.append(smaller_n)
return result
else:
return []
+ +

把上面的代码提交上去,还是正确的,但是超时了。 +该怎么优化呢?

+

遇到这样的问题,如果能够想出S(N)=f(S(N-1),S(N-2),...)这样的动态规划 +解法 +就可以把时间复杂度降到O(N)了。能不能使用动态规划呢? +还是要仔仔细细列出来,可是怎么也找不到递推规律。只好放弃。不过在思考 +的过程中忽然想到一种思路。对于数组[5, 2, 6, 1]],如果我先遍历 +求出5的smaller数组[2,1]和larger数组[6]。那么6的smaller个数怎么 +求呢,s(6) = len([5,2,6,1]) - pos(6) = 4-3 = 1。看起来很不错,但是当 +数的个数多起来,就变得复杂了,无法这样简单的求解了。真是非常的泄气啊。 +想了好久也没有想出新点子。脑子里把曾经用到的方法想了一个遍, +也还是不行。

+

用二维图描述问题

物理学科中经常使用的一个策略就是转换坐标系,这样可以神奇的 +化繁为简。那么我是否可以这样做呢。看看能不能用二维的图来重新展示这个问题。 +于是我就画了这样一个图表。这是对数组[5,1,6,7,2,3]画的表格。横轴表示数组的index, +纵轴表示大小。

+

2016-12-26-1

+

我把它画在纸上,盯着看了半小时。呵呵,真的有这么长时间。 +因为我一直还是老思路,总想着根据第一个数字把剩下的数组分成大小两批。 +后来我想,既然是二维图,我既可以从左向右,一列一列的看,也可以从上往下一行一行的 +看。 不错,终于有了新的思路。

+
    +
  1. 初始化一个长度为N的smaller数组,用于存放最后的解。
  2. +
  3. 对原始数组从大到小排序。
  4. +
  5. 取出最大的数,这个数的 value = smaller_num(Nmax) = 数组的长度 - pos(Nmax)
  6. +
  7. 保存结果smaller[index(Nmax)] = value
  8. +
  9. 对于index大于该数的所有数字的index减一。
  10. +
  11. 重复2
  12. +
+

如下图。首先找到最大的数字7,它的索引是4(第四个数字), +而数组的长度为6,那么smaller_num("7") = 6 - 4 = 2。然后不考虑该数字7。 +对7右侧的23同时向左移动,同时令数组的长度减一。然后依次这样操作。

+

2016-12-26-2

+

这样我们就可以写出一个新的算法。而对于步骤5则 +使用了简单实现,遍历整个数组,对所有index > index(Nmax)数的索引进行 +减一。上述算法描述没有考虑有多个最大值的情况。但是下面的代码里考虑了。

+
class Solution1(object):
def countSmaller(self, nums):
"""
O(N*N)
:type nums: List[int]
:rtype: List[int]
"""
origin = list(enumerate(nums))
l = self.create(nums)
l.sort(cmp=compare, reverse=True)
smaller = [0] * len(nums)
i = 0
N = len(nums)
while i < len(l):
v, origin_pos, cur_pos = l[i]
smaller[origin_pos] = N - 1 - cur_pos
equal_n = 0
for j in range(i + 1, len(nums)):
v1, origin_pos2, cur_pos2 = l[j]
if cur_pos2 > cur_pos:
l[j][2] = cur_pos2 - 1
if v1 == v:
# 考虑值相等的情况。
equal_n += 1

smaller[origin_pos] -= equal_n

N -= 1
i += 1

return smaller

def create(self, nums):
result = []
for i in range(len(nums)):
result.append([nums[i], i, i])
return result
+ +

终于通过了

事情终于有了进展,看起来很不错。但是仍然 +遇到了时间复杂度过高的问题。因为实现步骤5的方法导致整个程序的 +时间复杂度是O(N*N)。那么有什么办法能够优化呢。嗯,肯定有办法的。 +我们这样来做。

+
    +
  1. 保存一个原始索引的(origin_index,value)originList。
  2. +
  3. 再保存一个从大到小排序后的(origin_index,value)lList。
  4. +
  5. l根据value从大到小进行遍历:根据orgin_index使用二分查找 +查找origin中对应的实际索引,计算得到该值对应的smaller_num之后, +从origin中删除该项。
  6. +
+

因为二分查找和删除项的时间复杂度是O(logN)。 +上述步骤的总的时间复杂度是O(N*logN)。

+

而相等的数怎么办呢?嗯我们可以在排序 +的时候保证序列不但满足值的升序,还能保证索引值的升序。 +这样,原始数组中处于最右侧的最大数永远在序列的末尾。这个数的右侧不会有相等的值。 +因而计算的时候就不需要考虑相等值的情况。 +而该数字计算之后会进行删除。那么即使有相等的数,也不会受到影响。 +真是很不错啊。

+
class Solution11(object):
def countSmaller(self, nums):
"""
O(NlogN)
:type nums: List[int]
:rtype: List[int]
"""
origin = list(enumerate(nums))
l = self.create(nums)
l.sort(cmp=self.compare)
smaller = [0] * len(nums)
while l:
v, pos = l.pop()
cur_pos = self.bin_find(origin, pos)
smaller[pos] = len(origin) - 1 - cur_pos
origin.pop(cur_pos)

return smaller

def compare(self, x, y):
# 这样排序那么相等的元素的仍然顺序不变
if x[0] > y[0]:
return 1
elif x[0] == y[0]:
return 1
else:
return -1

def create(self, nums):
result = []
for i in range(len(nums)):
result.append([nums[i], i])
return result

def bin_find(self, origin, pos):
# 二分查找法
start = 0
end = len(origin)
mid = (start + end) // 2
new_mid = None
while mid < len(origin) and mid >= 0:

if origin[mid][0] == pos:
break
elif origin[mid][0] > pos:
end = mid
new_mid = (start + end) // 2
elif origin[mid][0] < pos:
start = mid
new_mid = (start + end) // 2

if new_mid == mid:
mid += 1
else:
mid = new_mid

return mid

+ +

总结

以上的解法是首先排序,再进行巧妙处理的一种方法。排序的 +时间复杂度是O(N*logN),接下来的处理也是O(N*logN)

+]]>
+ + leetcode + 算法 python - 技巧
@@ -343,99 +503,6 @@ cython fib : 0.00059495s Cython - - [leetcode 315]Count of Smaller Numbers After Self 原创解法 - /2016/12/26/leetcode-315/ - 题目概述

原题链接

-
You are given an integer array nums and you
-have to return a new counts array. 
-The counts array has the property where counts[i] is 
-the number of smaller elements to the right of nums[i].
-
-Example:
-
-Given nums = [5, 2, 6, 1]
-
-To the right of 5 there are 2 smaller elements (2 and 1).
-To the right of 2 there is only 1 smaller element (1).
-To the right of 6 there is 1 smaller element (1).
-To the right of 1 there is 0 smaller element.
-Return the array [2, 1, 1, 0].
-
- - -

拿到这个题,最先想到的是时间复杂度O(N*N) -的解法。那就实现一下吧。

-
class Solution2(object):
def countSmaller(self, nums):
"""
O(N*N)
:type nums: List[int]
:rtype: List[int]
"""
if nums:
n = len(nums)
result = []
for i in range(n):
cur_val = nums[i]
smaller_n = 0
for j in range(i, n):
if nums[j] < cur_val:
smaller_n += 1

result.append(smaller_n)
return result
else:
return []
- -

把上面的代码提交上去,还是正确的,但是超时了。 -该怎么优化呢?

-

遇到这样的问题,如果能够想出S(N)=f(S(N-1),S(N-2),...)这样的动态规划 -解法 -就可以把时间复杂度降到O(N)了。能不能使用动态规划呢? -还是要仔仔细细列出来,可是怎么也找不到递推规律。只好放弃。不过在思考 -的过程中忽然想到一种思路。对于数组[5, 2, 6, 1]],如果我先遍历 -求出5的smaller数组[2,1]和larger数组[6]。那么6的smaller个数怎么 -求呢,s(6) = len([5,2,6,1]) - pos(6) = 4-3 = 1。看起来很不错,但是当 -数的个数多起来,就变得复杂了,无法这样简单的求解了。真是非常的泄气啊。 -想了好久也没有想出新点子。脑子里把曾经用到的方法想了一个遍, -也还是不行。

-

用二维图描述问题

物理学科中经常使用的一个策略就是转换坐标系,这样可以神奇的 -化繁为简。那么我是否可以这样做呢。看看能不能用二维的图来重新展示这个问题。 -于是我就画了这样一个图表。这是对数组[5,1,6,7,2,3]画的表格。横轴表示数组的index, -纵轴表示大小。

-

2016-12-26-1

-

我把它画在纸上,盯着看了半小时。呵呵,真的有这么长时间。 -因为我一直还是老思路,总想着根据第一个数字把剩下的数组分成大小两批。 -后来我想,既然是二维图,我既可以从左向右,一列一列的看,也可以从上往下一行一行的 -看。 不错,终于有了新的思路。

-
    -
  1. 初始化一个长度为N的smaller数组,用于存放最后的解。
  2. -
  3. 对原始数组从大到小排序。
  4. -
  5. 取出最大的数,这个数的 value = smaller_num(Nmax) = 数组的长度 - pos(Nmax)
  6. -
  7. 保存结果smaller[index(Nmax)] = value
  8. -
  9. 对于index大于该数的所有数字的index减一。
  10. -
  11. 重复2
  12. -
-

如下图。首先找到最大的数字7,它的索引是4(第四个数字), -而数组的长度为6,那么smaller_num("7") = 6 - 4 = 2。然后不考虑该数字7。 -对7右侧的23同时向左移动,同时令数组的长度减一。然后依次这样操作。

-

2016-12-26-2

-

这样我们就可以写出一个新的算法。而对于步骤5则 -使用了简单实现,遍历整个数组,对所有index > index(Nmax)数的索引进行 -减一。上述算法描述没有考虑有多个最大值的情况。但是下面的代码里考虑了。

-
class Solution1(object):
def countSmaller(self, nums):
"""
O(N*N)
:type nums: List[int]
:rtype: List[int]
"""
origin = list(enumerate(nums))
l = self.create(nums)
l.sort(cmp=compare, reverse=True)
smaller = [0] * len(nums)
i = 0
N = len(nums)
while i < len(l):
v, origin_pos, cur_pos = l[i]
smaller[origin_pos] = N - 1 - cur_pos
equal_n = 0
for j in range(i + 1, len(nums)):
v1, origin_pos2, cur_pos2 = l[j]
if cur_pos2 > cur_pos:
l[j][2] = cur_pos2 - 1
if v1 == v:
# 考虑值相等的情况。
equal_n += 1

smaller[origin_pos] -= equal_n

N -= 1
i += 1

return smaller

def create(self, nums):
result = []
for i in range(len(nums)):
result.append([nums[i], i, i])
return result
- -

终于通过了

事情终于有了进展,看起来很不错。但是仍然 -遇到了时间复杂度过高的问题。因为实现步骤5的方法导致整个程序的 -时间复杂度是O(N*N)。那么有什么办法能够优化呢。嗯,肯定有办法的。 -我们这样来做。

-
    -
  1. 保存一个原始索引的(origin_index,value)originList。
  2. -
  3. 再保存一个从大到小排序后的(origin_index,value)lList。
  4. -
  5. l根据value从大到小进行遍历:根据orgin_index使用二分查找 -查找origin中对应的实际索引,计算得到该值对应的smaller_num之后, -从origin中删除该项。
  6. -
-

因为二分查找和删除项的时间复杂度是O(logN)。 -上述步骤的总的时间复杂度是O(N*logN)。

-

而相等的数怎么办呢?嗯我们可以在排序 -的时候保证序列不但满足值的升序,还能保证索引值的升序。 -这样,原始数组中处于最右侧的最大数永远在序列的末尾。这个数的右侧不会有相等的值。 -因而计算的时候就不需要考虑相等值的情况。 -而该数字计算之后会进行删除。那么即使有相等的数,也不会受到影响。 -真是很不错啊。

-
class Solution11(object):
def countSmaller(self, nums):
"""
O(NlogN)
:type nums: List[int]
:rtype: List[int]
"""
origin = list(enumerate(nums))
l = self.create(nums)
l.sort(cmp=self.compare)
smaller = [0] * len(nums)
while l:
v, pos = l.pop()
cur_pos = self.bin_find(origin, pos)
smaller[pos] = len(origin) - 1 - cur_pos
origin.pop(cur_pos)

return smaller

def compare(self, x, y):
# 这样排序那么相等的元素的仍然顺序不变
if x[0] > y[0]:
return 1
elif x[0] == y[0]:
return 1
else:
return -1

def create(self, nums):
result = []
for i in range(len(nums)):
result.append([nums[i], i])
return result

def bin_find(self, origin, pos):
# 二分查找法
start = 0
end = len(origin)
mid = (start + end) // 2
new_mid = None
while mid < len(origin) and mid >= 0:

if origin[mid][0] == pos:
break
elif origin[mid][0] > pos:
end = mid
new_mid = (start + end) // 2
elif origin[mid][0] < pos:
start = mid
new_mid = (start + end) // 2

if new_mid == mid:
mid += 1
else:
mid = new_mid

return mid

- -

总结

以上的解法是首先排序,再进行巧妙处理的一种方法。排序的 -时间复杂度是O(N*logN),接下来的处理也是O(N*logN)

-]]>
- - leetcode - 算法 - python - -
解决python3使用system-site-packages创建虚拟环境时没有pip的问题 /2017/01/18/2017-1-18-venv-no-pip/ @@ -510,73 +577,6 @@ setuptools 28.8.0 venv - - [leetcode 327]Count of Range Sum 原创解法 - /2016/12/31/leetcode-327/ - 题目概述

原题链接

-
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
-Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.
-
-Example:
-Given nums = [-2, 5, -1], lower = -2, upper = 2,
-Return 3.
-The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
-
- - -

O(N^2)解法

最直接的想法当然就是遍历求和了。但是这样是无法通过测试的。

-
class Solution3(object):
def countRangeSum(self, nums, lower, upper):
"""
:type nums: List[int]
:type lower: int
:type upper: int
:rtype: int
"""

sums = []

for i in range(len(nums)):
s = 0
for j in range(i, len(nums)):
s += nums[j]
if lower <= s <= upper:
sums.append(s)
return len(sums)

- -

死亡凝视

必须要想出复杂度是Nlog(N)的算法才行。但是问题也很明显, -要想找出所有的s(i,j) for 0 <= i <= j < n and lower <= s(i,j) <= upper -那必须要进行遍历啊,因此时间复杂度必须是N^2。 -啊啊,实在想不出来了。

-

第二天,既然要求时间复杂度为NlogN那么就必然不能遍历求出s(i,j),但是 -题目要求的是求出Num(s(i,j)) for 0 <= i <= j < n and lower <= s(i,j) <= upper -,也就是说给出个数就行,因此确实不一定要给出所有的s(i,j)。嗯,有点进展了。

-

第三天,排序算法的复杂度是NlogN,怎么才能依靠排序算法求出呢。难道对所有的 -s(i,j)进行排序么?这样肯定不行。 -能否使用老办法画图呢?这样好观察一下。但是仔细想了想,这是一个三维图。 -纸上画不出来。 -只好画一个二维图吧。下图是对数组[5,7,8,-2,1]画的二维表格。格子 -中的数字就是s(i,j)

-

327-1

-

我仔细的盯着看。我给这种解决问题的方式,起了 -个名字——死亡凝视。看过The Big Bang的朋友可能记得SheldonRaj一起 -工作的样子,就是一动不动的看着黑板上的公式,看上一整天。

-

327-2

-

嘿嘿,你们不要笑啊,我的方式差不多。只不过是盯着表格。这样的表格 -其实我画了很多。但是只有这个我看出了规律。

-

327-3

-

看出来了么,三列带颜色的数字。是不是发现它们有相同的趋势呢? -是的它们有相同的趋势。

-

如果没有看出来。我再画一个图。看看这三列数字和蓝色数字的关系。

-

327-4

-

是的。20 - 5 = 1518 - 5 = 1319 - 5 = 14。 -假如上下区间为[12,18],那么对于排序后的第一列[5,12,18,19,20], -可以使用二分查找法找出上限的位置是2,下限的位置为1,个数为2。 -那么第二列[7,15,13,14][12,18]的数字的个数为3。 -怎么根据第一列排序后的数字求出呢。嗯,很简单。 -我们把上下限加上5,变成17, 23,这样就可以发现[5,12,18,19,20]找出 -个数为3。嗯,哈哈。真是不错。这样子,就不需要求出所有的s(i,j)了。

-

但是,别着急,还是有问题,我们需要在计算第二列的个数的之前,把第一列的数字中的5排除掉。 -否则的话,如果区间是[-100,100],那么我们就多计算了。

-

通过的代码

# python3.5
class Solution4(object):
def countRangeSum(self, nums, lower, upper):
"""
思路:对 list0 = [s(0,j) for j in (0,n)]先进行排序。
list1 = [s(1,j) for j in (1,n)],可以由 list0生成。

:type nums: List[int]
:type lower: int
:type upper: int
:rtype: int
"""
if not nums:
return 0
else:
result = 0
n = len(nums)
l = []
l2 = []
sum = 0
for i in range(0, n):
sum += nums[i]
l.append((sum, i))
l2.append((sum, i))
l.sort()
for i in range(0, n):
if i > 0:
lower += nums[i - 1]
upper += nums[i - 1]
lower_pos = self.find_lower(l, lower)
upper_pos = self.find_uppper(l, upper)
if lower_pos is not None and upper_pos is not None:
result += upper_pos - lower_pos + 1
pos = self.find_lower(l, l2[i][0])
l.pop(pos) # <-- 这里是个接近O(N)的操作

return result

def find(self, nums, target):
n = len(nums)

start = 0
end = n
mid = n // 2
new_mid = None

while 0 <= mid < n:
if nums[mid][0] == target:
break
elif nums[mid][0] < target and mid + 1 < n and nums[mid + 1][0] > target:
break
elif nums[mid][0] < target:
start = mid
new_mid = (start + end) // 2
elif nums[mid][0] > target:
end = mid
new_mid = (start + end) // 2
if new_mid == mid:
mid += 1
else:
mid = new_mid

return mid if nums[mid][0] == target else mid + 1

def find_lower(self, nums, lower):
n = len(nums)
if lower > nums[n - 1][0]:
return None
elif lower < nums[0][0]:
return 0
else:
mid = self.find(nums, lower)
while mid - 1 >= 0 and nums[mid - 1][0] == lower:
mid -= 1
return mid

def find_uppper(self, nums, upper):
n = len(nums)
if upper > nums[n - 1][0]:
return n - 1
elif upper < nums[0][0]:
return None
else:
mid = self.find(nums, upper)
while mid < n - 1 and nums[mid + 1][0] == upper:
mid += 1
return mid if nums[mid][0] == upper else mid - 1
- -

总结

排序操作的时间复杂度是O(NlogN),而找出每列一列的个数的时间复杂度是O(logN) -因为一共有N列,所以时间复杂度为O(NlogN),另外 -我们在代码中使用了对List数据的非尾部的pop操作,这个操作的时间 -复杂度接近O(N),严格来说,我们的代码的时间复杂度还是O(N^2)。如果能够不使用这个操作。 -我们的代码速度会更快。

-
l.pop(pos) # <-- 这里是个接近O(N)的操作
-
-]]>
- - leetcode - 算法 - python - -
[leetcode 295]Find Median from Data Stream 原创解法 /2017/01/31/2017-1-31-leetcode-295/ @@ -824,86 +824,40 @@ compile_clojure: [java] Compiling clojure.main to d:\work\idea\clojure-20081217\classes [java] Compiling clojure.set to d:\work\idea\clojure-20081217\classes [java] Compiling clojure.xml to d:\work\idea\clojure-20081217\classes - [java] Compiling clojure.zip to d:\work\idea\clojure-20081217\classes - [java] Compiling clojure.inspector to d:\work\idea\clojure-20081217\classes - -jar: - [jar] Building jar: d:\work\idea\clojure-20081217\clojure.jar - -BUILD SUCCESSFUL -Total time: 13 seconds - -

运行

官方readme给出了办法来证明运行成功。执行java -cp clojure.jar clojure.lang.Repl -,这样就可以进入clojure的repl环境。

-
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Repl
-Clojure
-user=> (def n 2)
-#'user/n
-user=> (* n 2)
-4
-user=>
-
-

如何运行一个clj文件呢?可以这样做。

- -

如下所示。

-
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Script hello_world.clj
-hello world
-
-

成功了。

-]]> - - java - clojure - jvm - -
- - How to call java code in Grails 3.2.6 - /2017/03/06/2017-3-6-grails-call-java/ - 解决方法

我的Grails的环境是

-
grails -v
-| Grails Version: 3.2.6
-| Groovy Version: 2.4.7
-| JVM Version: 1.8.0_71
-
- - -

grails 3.2.6是用gradle进行构建的。所以如果要添加java类, -就需要修改build.gradle

-

第一步

创建src/main/java目录。对于com.yanggeorge.XMLtest类, -则要创建src/main/java/com/yanggeorge/目录,并把XMLtest.java放在该 -路径下。

-

第二步

build.gradle文件中添加如下代码

-
apply plugin: "java"

task compileOne (type: JavaCompile) {
source = sourceSets.main.java.srcDirs
include 'com/yanggeorge/XMLtest.java'
classpath = sourceSets.main.compileClasspath
destinationDir = sourceSets.main.output.classesDir
}

compileOne.options.compilerArgs = ["-sourcepath", "$projectDir/src/main/java"]
- -

第三步

编译XMLtest.java。可以用grails compile进行编译。

-
D:\work\grails>grails compile
-:compileJava UP-TO-DATE
-:compileGroovy UP-TO-DATE
-:buildProperties
-:processResources UP-TO-DATE
-:classes UP-TO-DATE
-
-BUILD SUCCESSFUL
-
-Total time: 23.638 secs
-D:\work\grails>
-
-

第四步

修改grails-app/conf/spring/resources.groovy

-
import com.yanggeorge.XMLtest

beans = {
myXMLtest(XMLtest)
}
+ [java] Compiling clojure.zip to d:\work\idea\clojure-20081217\classes + [java] Compiling clojure.inspector to d:\work\idea\clojure-20081217\classes -

第五步

已经可以使用myXMLtest了,例如创建一个service,grails-app/services/rss/RssService.groovy -第6行就是依赖注入的bean。

-
import grails.transaction.Transactional

@Transactional
class RssService {

def myXMLtest

def serviceMethod(String url, String keyword) {
def items = myXMLtest.getAllItems(url)

}

}
+jar: + [jar] Building jar: d:\work\idea\clojure-20081217\clojure.jar +BUILD SUCCESSFUL +Total time: 13 seconds + +

运行

官方readme给出了办法来证明运行成功。执行java -cp clojure.jar clojure.lang.Repl +,这样就可以进入clojure的repl环境。

+
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Repl
+Clojure
+user=> (def n 2)
+#'user/n
+user=> (* n 2)
+4
+user=>
+
+

如何运行一个clj文件呢?可以这样做。

+ +

如下所示。

+
d:\work\idea\clojure-20081217>java -cp clojure.jar clojure.lang.Script hello_world.clj
+hello world
+
+

成功了。

]]>
java - grails - gradle + clojure + jvm
@@ -1003,6 +957,107 @@ gradle run build + + How to call java code in Grails 3.2.6 + /2017/03/06/2017-3-6-grails-call-java/ + 解决方法

我的Grails的环境是

+
grails -v
+| Grails Version: 3.2.6
+| Groovy Version: 2.4.7
+| JVM Version: 1.8.0_71
+
+ + +

grails 3.2.6是用gradle进行构建的。所以如果要添加java类, +就需要修改build.gradle

+

第一步

创建src/main/java目录。对于com.yanggeorge.XMLtest类, +则要创建src/main/java/com/yanggeorge/目录,并把XMLtest.java放在该 +路径下。

+

第二步

build.gradle文件中添加如下代码

+
apply plugin: "java"

task compileOne (type: JavaCompile) {
source = sourceSets.main.java.srcDirs
include 'com/yanggeorge/XMLtest.java'
classpath = sourceSets.main.compileClasspath
destinationDir = sourceSets.main.output.classesDir
}

compileOne.options.compilerArgs = ["-sourcepath", "$projectDir/src/main/java"]
+ +

第三步

编译XMLtest.java。可以用grails compile进行编译。

+
D:\work\grails>grails compile
+:compileJava UP-TO-DATE
+:compileGroovy UP-TO-DATE
+:buildProperties
+:processResources UP-TO-DATE
+:classes UP-TO-DATE
+
+BUILD SUCCESSFUL
+
+Total time: 23.638 secs
+D:\work\grails>
+
+

第四步

修改grails-app/conf/spring/resources.groovy

+
import com.yanggeorge.XMLtest

beans = {
myXMLtest(XMLtest)
}
+ +

第五步

已经可以使用myXMLtest了,例如创建一个service,grails-app/services/rss/RssService.groovy +第6行就是依赖注入的bean。

+
import grails.transaction.Transactional

@Transactional
class RssService {

def myXMLtest

def serviceMethod(String url, String keyword) {
def items = myXMLtest.getAllItems(url)

}

}
+ +]]>
+ + java + gradle + grails + +
+ + How to resize VirtualBox fixed image size and keep contents unchanged ? + /2017/07/09/2017-7-9-resize-fixed-image/ + 问题

如何更改镜像文件的大小呢,尤其是如何把一个固定大小的镜像变大,并且之前的内容完全不改变。 +我尝试了一些方法,比如下面的几个步骤。1.查看信息,2.克隆新的vdi,3.调整大小,4.再查看信息。

+ + +
+

VBoxManage showhdinfo mydisk.vdi
VBoxManage clonehd mydisk.vdi mydiskClone.vdi
VBoxManage modifyhd mydiskClone.vdi –resize 61440
VBoxManage showhdinfo mydiskClone.vdi

+
+

这个方法的确是调整了大小,不过镜像文件变成了dynamic size而不是fixed size。这还好了, +最麻烦的是,当使用新的vdi的时候,发现自己的用户已经无法登陆,只能使用root用户登陆了。 +这是无法接受的。

+

用GParted实现

幸运的是有人给出了详细的方法。

+
+

resize-method

+
+

我来解释一下,其中最核心的就是

+
dd if=/dev/sda of=/dev/sdb
+
+

执行完成这个语句之后,不要进行分区等任何操作。 +(虽然看到新增加的磁盘处于未分配的状态。)直接单独挂载这个磁盘启动。 +果然可以登陆,一切照旧。但是只是这样并没有完成磁盘空间变大。只是实现了内容的复制。 +要想扩大,就需要使用GParted进行resize了。重新使用GParted Live CD进入新磁盘的分区图形界面, +可以发现新磁盘的某些空间已经进行分区,但是还有部分空间没有进行分区。 +在我这里是这样显示的

+
+ ------------------------------------------------------
+ |     sda            |  swap  |     unallocated      |   
+ ------------------------------------------------------
+
+ +
    +
  1. 首先删掉swap交换分区。这样/dev/sda分区紧接着就是未分配的空间了。
  2. +
  3. 接着resize sda分区。留几百兆做swap交换分区。
  4. +
  5. 最后创建一个交换分区。
  6. +
+

最后重启。应该会发现无法登陆,出现这样一个提示 +a start job is running for dev-disk-by…。耐心等待一下,发现还是可以正常登陆的。 +而且磁盘的空间变大了。一切似乎不错。

+

解决最后的小问题

重启一下看看,发现还是有a start job is running for dev-disk-by…这个提示, +需要等待一两分钟,真是糟糕。别担心,这是因为重新创建了swap交换分区的缘故。有人已经给出了解决办法。

+
+

To avoid the issue, in /etc/fstab you can either +Replace the swap UUID with the new one (run sudo blkid to find it) after the primary partition resizing.

+
+

reset-swap-uuid

+

重启,问题解决了。

+]]>
+ + image + resize + virutalbox + +
宏扩展的执行逻辑初探(一) /2018/11/09/minilisp/ @@ -1071,61 +1126,6 @@ gradle run macro - - How to resize VirtualBox fixed image size and keep contents unchanged ? - /2017/07/09/2017-7-9-resize-fixed-image/ - 问题

如何更改镜像文件的大小呢,尤其是如何把一个固定大小的镜像变大,并且之前的内容完全不改变。 -我尝试了一些方法,比如下面的几个步骤。1.查看信息,2.克隆新的vdi,3.调整大小,4.再查看信息。

- - -
-

VBoxManage showhdinfo mydisk.vdi
VBoxManage clonehd mydisk.vdi mydiskClone.vdi
VBoxManage modifyhd mydiskClone.vdi –resize 61440
VBoxManage showhdinfo mydiskClone.vdi

-
-

这个方法的确是调整了大小,不过镜像文件变成了dynamic size而不是fixed size。这还好了, -最麻烦的是,当使用新的vdi的时候,发现自己的用户已经无法登陆,只能使用root用户登陆了。 -这是无法接受的。

-

用GParted实现

幸运的是有人给出了详细的方法。

-
-

resize-method

-
-

我来解释一下,其中最核心的就是

-
dd if=/dev/sda of=/dev/sdb
-
-

执行完成这个语句之后,不要进行分区等任何操作。 -(虽然看到新增加的磁盘处于未分配的状态。)直接单独挂载这个磁盘启动。 -果然可以登陆,一切照旧。但是只是这样并没有完成磁盘空间变大。只是实现了内容的复制。 -要想扩大,就需要使用GParted进行resize了。重新使用GParted Live CD进入新磁盘的分区图形界面, -可以发现新磁盘的某些空间已经进行分区,但是还有部分空间没有进行分区。 -在我这里是这样显示的

-
- ------------------------------------------------------
- |     sda            |  swap  |     unallocated      |   
- ------------------------------------------------------
-
- -
    -
  1. 首先删掉swap交换分区。这样/dev/sda分区紧接着就是未分配的空间了。
  2. -
  3. 接着resize sda分区。留几百兆做swap交换分区。
  4. -
  5. 最后创建一个交换分区。
  6. -
-

最后重启。应该会发现无法登陆,出现这样一个提示 -a start job is running for dev-disk-by…。耐心等待一下,发现还是可以正常登陆的。 -而且磁盘的空间变大了。一切似乎不错。

-

解决最后的小问题

重启一下看看,发现还是有a start job is running for dev-disk-by…这个提示, -需要等待一两分钟,真是糟糕。别担心,这是因为重新创建了swap交换分区的缘故。有人已经给出了解决办法。

-
-

To avoid the issue, in /etc/fstab you can either -Replace the swap UUID with the new one (run sudo blkid to find it) after the primary partition resizing.

-
-

reset-swap-uuid

-

重启,问题解决了。

-]]>
- - image - resize - virutalbox - -
[leetcode 352]原创解法 /2018/11/08/2018-11-8-leetcode-352/ @@ -1298,123 +1298,28 @@ has_next和next方法本质上是一样的,可以只保留一个next就可以

实现发布

两个步骤,

-

参考了Bittorrent udp-tracker protocol extension -因为对入参和返回参数说明的更详细一些。用的tracker是一个热门的磁力链接。

-
ip = 'exodus.desync.com'  # get from tracker list
-port = 6969  # get from tracker list
-
-

而info_hash是用uTorrent软件打开该磁力链接后获得的。

-
1aa4c13830b822c1375686d685a9fce23405f6ba
-
-

以下是代码实现。uTorrent软件打开磁力链接会拿到tracker列表。

-

announce的重要参数说明

-
    -
  1. num_want : -1 # 希望获得的peer个数,则是默认的peer个数
  2. -
  3. ip : 0 # 当前发送udp包的地址
  4. -
  5. port : 监听的公网端口
  6. -
-

一旦发布完成,该监听的端口会收到DHT协议的请求,例如FIND_NODE

-
# -*- coding: utf-8 -*-

__author__ = 'ym'
"""
Date : '2019/1/8'
Description : udp tracker protocol

"""
import socket
import struct


def ip_me():
import psutil
import socket

def get_ip_addresses(family):
for interface, snics in psutil.net_if_addrs().items():
for snic in snics:
if snic.family == family:
yield (interface, snic.address)

addrs = list(get_ip_addresses(socket.AF_INET))
for tup in addrs:
if tup[0] == 'en0':
return tup[1]


SOURCE_IP = ip_me()
SOURCE_PORT = 59876


def to_byte(integer, length=4):
return integer.to_bytes(length, byteorder='big')


def udp_send(ip, port, data):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((SOURCE_IP, SOURCE_PORT))
sock.sendto(data, (ip, port))


def udp_recv(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((ip, port))
while True:
data, addr = sock.recvfrom(2048)
print("received message:", data)
return data


class UdpAnnouceRequest(object):
def __init__(self):
self.connection_id = None
self.action = None
self.transaction_id = None
self.info_hash = None
self.peer_id = None
self.downloaded = None
self.left = None
self.uploaded = None
self.event = 0
self.ip = None
self.key = None
self.num_want = -1
self.port = None
self.extensions = None

def get_bytes(self):
result = to_byte(self.connection_id, 8)
result += to_byte(self.action)
result += to_byte(self.transaction_id)
result += self.info_hash
result += self.peer_id
result += to_byte(self.downloaded, 8)
result += to_byte(self.left, 8)
result += to_byte(self.uploaded, 8)
result += to_byte(self.event)
result += to_byte(self.ip)
result += to_byte(self.key)
result += struct.pack(">i", self.num_want)
result += to_byte(self.port, 2)
return result


def udp_announcing(request: UdpAnnouceRequest, dst_ip, dst_port):
data = request.get_bytes()
udp_send(dst_ip, dst_port, data)


def connect(tracker_ip, tracker_port):
protocol_id = 0x41727101980 # magic number
action = 0 # connect
transaction_id = 123 # random

data = struct.pack(">Qii", protocol_id, action, transaction_id)
udp_send(tracker_ip, tracker_port, data)

reply = udp_recv(SOURCE_IP, SOURCE_PORT)
action, transaction_id, connection_id = struct.unpack(">iiQ", reply)
fmt = "connect reply:\n\taction:{}, transaction_id:{}, connection_id:{}"
print(fmt.format(action, transaction_id, connection_id))
return connection_id


def announce(connection_id, dst_ip, dst_port):
request = UdpAnnouceRequest()
request.connection_id = connection_id
request.action = 1
request.transaction_id = 1234 # random
request.info_hash = bytearray.fromhex('1aa4c13830b822c1375686d685a9fce23405f6ba') # compute from meta_info
request.peer_id = bytearray.fromhex('ab' * 20) # random
request.downloaded = 0
request.left = 1509949440 # from meta_info
request.uploaded = 0
request.ip = 0 # default
request.key = 314
request.port = 59601 #
# print(request)
udp_announcing(request, dst_ip, dst_port)
return udp_recv(SOURCE_IP, SOURCE_PORT)


def udp_process():
ip = 'exodus.desync.com' # get from tracker list
port = 6969 # get from tracker list
connection_id = connect(ip, port)
reply = announce(connection_id, ip, port)
action, transaction_id, interval, leechers, seeders = struct.unpack(">iiiii", reply[0:20])
fmt = "announce_reply:\n\taction:{}, transaction_id:{}, interval:{}s, leechers:{}, seeders:{}"
print(fmt.format(action, transaction_id, interval, leechers, seeders))
peers = []
for i in range(20, len(reply), 6):
if i + 6 <= len(reply):
data = reply[i:i + 6]
n1, n2, n3, n4, peer_port = struct.unpack(">BBBBH", data)
peer_ip = "{}.{}.{}.{}".format(n1, n2, n3, n4)
peers.append((peer_ip, peer_port))
print("get {} peers:{}".format(len(peers), peers))


if __name__ == '__main__':
udp_process()
- -

运行结果

运行日志如下,可以看到拿到了200个peers的ipv4地址和端口。

-
/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/udp_announce.py
received message: b'\x00\x00\x00\x00\x00\x00\x00{\xd89\xa8\x9dv\xd3=]'
connect reply:
action:0, transaction_id:123, connection_id:15580669780121828701
received message: b'\x00\x00\x00\x01\x00\x00\x04\xd2\x00\x00\x07_\x00\x00\x00%\x00\x00\x01qi\xe8\xff\xd8XWl\x18-F\xbcd\x9e\xae}\xea\xaa[\xd41S\'\xcb\x0eb\x14\x8f\xcfbcg\xcd\xaf9\xe0\x1f-\xf7*\xa6\xa2\x86)P\xadl\xbc\xbf)\xb6{\xc4w\xaa)\xcd\xf0@cF--;M\xa6Wp\xce\xc7\xff\xfc\xcf\xbb\x9e\x18\x9c\xec\x96\xc4\xcf\x83PS\x06_\xb4zmc\xd3\x82i\xef\x0b\xeb\xd0\xaf ~d\xf3\x8c\xde\x9b?\xfc\xdb!1\x93W\x0b\xc3I\xad\xf4,)\xf0\x1c\xc5\xe0\x98\x80\xbbZ\xb4^UA#\'|\x95\x7fd\xc77p\xc8Z\xadN\xb5p\xc7K\xbc\xd31i\x9c\xe0\x7f0\xaeV\x02\xc2\xe3G.R\x92\xfc\x15\xc8\x05R\x18\x18I\x943H3~\r\xd3\x89=\x06\xe6\xbb86-y\xd1\xa6\xca\xcb)\xd7\r\x8aLo)\xcb\xda\x8e;K)Z<\x9c\xb0\x87);Q\xd0\xb2\x8b\x18\xa5\xc2T\xb8\xd7\x05\x90;\x83\xf1^\xca\x8eeB\xa1a\xb9\xe8\x15z\x83\xde\x93\n\xba\x91+\x9em\xb1\x9b\xd0V\x82Y\xf2\xb5\xad\xc7\rV\x06\xf8^k\x14R\x8eP\xb1\xfb\x15Rf\x1eV\xd0\x05R-\xd9\xf5\xc2"@\x86\xf0K\x00\x00@B\xdeFa*\x1f;\x02<\xa2\xa7\x18l\xed\xfa\xd2\xf4\x05k#\x81\xe8\xa5\x01\xban\x82\xcd\t\xd57\xb8\x8eQ\xdd\xd4\x1b\x13\xfalh\xd0\x83\xa5J\xa9\xb5\xce\xe7x:\xb8\xe1\xc9\xab\xc6\xf9\xa3:\xc5\xed\xc5\xe4\xd2c\xb6\xb9\x1cL\xc0\x19\x8b<\xbfn\x9a\xc2\x80\xcc\xf3\xc3?\xefn\x8dg\xa5=\x0cm\xb1^\xfcU\xf3[}\xdd\x90\x82yS\xe9o\xf3\xf0R@w\xc0\xf4\'\xae\xd0SR\xcc\xde@\xc5\xf8\xdb\x9e\x8a\x0f\xb3\x08_\x90\xb4\xc1\xa9=\xda$\x1a\xe1n\xe8V\r\xe5\xe1b\x8cr{1\xc3^\x05\xe8bR\x00Sn\xe5\x7fe\xe5G4V5I \x1bjQ\x9a\x80\x7f\xce\xe7y\xd5\xde]\xbc\x81D\xbd\xbf\xf1\xb8K\xdfZ\x8d\xe5\xa9=\xda#\x1a\xe1sE\xbb\xb8\xd4Oi\xa7\x10V\xe9\xffgG\x10\xf3\x8f\xde^\xea#S+%\\\xeeN_\xba\xbd3\x07g\xaf\xdb\xb3)\xa1:#\xe0G%\x98\xce\x84}\xe2\xd8ch\xeaN\xb2\xbe\xabp\xe3\xd2?\xb7\r\xcd\x9d\xf3\x8c\xa8\x01\x18I\xabH\x90\x84,A\x9a\x02}\xd1\\\xdd\x94bp\x8d:\xa7e:p\n\x17\x93\xb0\xb9h\x05\xa9)b\xdc\\\xed\x0f;]\x9dX\x10\x96\x98u\x92R\x18\x8a+\xc2LR\x0cH\xa7z\x06>\x96OI2\x9f.\xb6\xbax9\xee)\xbaS\x8b\x9b\x81)\x8b\xfa&m,)P\xe4\x95\xc8s%\x8e\x03+>\x1e\xc5\xf8\x92\xba\xb9\xca\xc4\xca\xa9\xba\xd3(\xc3Yxr\x07_\xbe:\x08\x08\xc8\x9c\xb2P\x14X\x7f\xcf\x9aIWU\xc6G\x80j\xa0\x9dRSx\xf4n\x07w\xe4m\xbe\xd4`\xc8\xd5g\xf4\xf2}\xe1\x01g5I%\xc1\xa1\\c@\x89\x99\x9bVSE\x95\xfb\xdeV\x12\xe0F\'gR\x17n\x90\x91\xf4O\xb7c\xa6\xd1\xfcOC9\xf0s\xee+\xf6\xc8u\xe1\xed+\xf1\x91DGD)Zo\x9e\xf3y\x18\xcb\x97\x9e=\'\xc4\xca\xa9\xba\xcbO\xa5\xe7(\x0e8\xc1\xa2\xf6k\x92\x14\x82q\x14b.\xe7oi\xe8X\x86\xe2\xbagF\xcc\xc2\xed\x83`+\xbdk\x99\xdfR\x07u\xa2\xbf\x8cBW\xcfc\x00\x00?\x8f\xecq\x00\x00)\xd2\n\xe3\xe9\xca\x1f-g\x8a\xe5y\xde\xb8\xe8\xad\xab\xc4\xd8\x97\xb8\x9f\xd0d\xd0g\xe9\xa2\xdb\xbd\xc4\xd1\xe9\xa4\x86\xc2\xc4\xcf\xb83\xe2\xaa\xc3\x93\xb5\xbe&\xfb\x9c\xde.\xdc\xd2\x14\x9aF9B\xa4{p\xcd\x9d\xf6\xe0\xcai\xb8\x99u\xf0+g{$\xb5\xb3LZ\xdbs\xab\x99~Qd\xff\xdf\xe8\xefP\'\x86\xc9:\x8aMD\xf3F\x88\xb8H\xe5%G\x84M1\x92\r~\xe9\x13)OB\xe1Vi\xdd\xcb\x16\xf53\xb3\xba\x96\x86\xaf\xba:\xb2\xa6!$\x7f\xdd|k\xc0+\x8a}{\xf5\x14\x8eAVy68&K\xb1tZ`\nY\xb8l\x04\xd7\xfb\x84\xb3h\xcfSb*\x93`\x17\xb5[\xebGV\x89\x15S\x91\xd1KA\xd7\xc4\xa3o?\xf5:\x9b\x83\x1d.\xd1,c\x05\x16\xb6\xba\xfd\xab97\xb2\x9d\xf9>g\xeb\x82i\xa4{G\xb7i\xe8V1\x91\xafg\xfc\xca\x96\xaf\x16g\x04\xf8\x8a\xda!_^\x9d\xaf\xd2-^\xaf\x94\x0c\xbc\x0f^L\x0f,\xc3\x00Y\xedbW\xf5\x84WFe\xc7\xaeAV\x19|\x9e\xb0\xcfH\xe6\xda\x88<\xdcFA\xe3\x15q\x08)\x8b\xf3:L\x1b)<\xe9\xf5\xaa[\x1f\xd7\x0e\x08Z\xb2\x01\t\x87,R\xd9\xcb\xd4\x9bSx"\xca\x8e[f`N\xc5\xed\x81\x96b\xdc\xc5\xe8\x1av#\'\xc5\xe8\x13n\xdbn\xc4\xf9g\xc4:\x15\xbbp|\xa6#\'\xb7W\xa9\x9b@5u\xc2\xdcQd\xef'
announce_reply:
action:1, transaction_id:1234, interval:1887s, leechers:37, seeders:369
get 200 peers:[('105.232.255.216', 22615), ('108.24.45.70', 48228), ('158.174.125.234', 43611), ('212.49.83.39', 51982), ('98.20.143.207', 25187), ('103.205.175.57', 57375), ('45.247.42.166', 41606), ('41.80.173.108', 48319), ('41.182.123.196', 30634), ('41.205.240.64', 25414), ('45.45.59.77', 42583), ('112.206.199.255', 64719), ('187.158.24.156', 60566), ('196.207.131.80', 21254), ('95.180.122.109', 25555), ('130.105.239.11', 60368), ('175.32.126.100', 62348), ('222.155.63.252', 56097), ('49.147.87.11', 49993), ('173.244.44.41', 61468), ('197.224.152.128', 47962), ('180.94.85.65', 8999), ('124.149.127.100', 50999), ('112.200.90.173', 20149), ('112.199.75.188', 54065), ('105.156.224.127', 12462), ('86.2.194.227', 18222), ('82.146.252.21', 51205), ('82.24.24.73', 37939), ('72.51.126.13', 54153), ('61.6.230.187', 14390), ('45.121.209.166', 51915), ('41.215.13.138', 19567), ('41.203.218.142', 15179), ('41.90.60.156', 45191), ('41.59.81.208', 45707), ('24.165.194.84', 47319), ('5.144.59.131', 61790), ('202.142.101.66', 41313), ('185.232.21.122', 33758), ('147.10.186.145', 11166), ('109.177.155.208', 22146), ('89.242.181.173', 50957), ('86.6.248.94', 27412), ('82.142.80.177', 64277), ('82.102.30.86', 53253), ('82.45.217.245', 49698), ('64.134.240.75', 0), ('64.66.222.70', 24874), ('31.59.2.60', 41639), ('24.108.237.250', 54004), ('5.107.35.129', 59557), ('1.186.110.130', 52489), ('213.55.184.142', 20957), ('212.27.19.250', 27752), ('208.131.165.74', 43445), ('206.231.120.58', 47329), ('201.171.198.249', 41786), ('197.237.197.228', 53859), ('182.185.28.76', 49177), ('139.60.191.110', 39618), ('128.204.243.195', 16367), ('110.141.103.165', 15628), ('109.177.94.252', 22003), ('91.125.221.144', 33401), ('83.233.111.243', 61522), ('64.119.192.244', 10158), ('208.83.82.204', 56896), ('197.248.219.158', 35343), ('179.8.95.144', 46273), ('169.61.218.36', 6881), ('110.232.86.13', 58849), ('98.140.114.123', 12739), ('94.5.232.98', 20992), ('83.110.229.127', 26085), ('71.52.86.53', 18720), ('27.106.81.154', 32895), ('206.231.121.213', 56925), ('188.129.68.189', 49137), ('184.75.223.90', 36325), ('169.61.218.35', 6881), ('115.69.187.184', 54351), ('105.167.16.86', 59903), ('103.71.16.243', 36830), ('94.234.35.83', 11045), ('92.238.78.95', 47805), ('51.7.103.175', 56243), ('41.161.58.35', 57415), ('37.152.206.132', 32226), ('216.99.104.234', 20146), ('190.171.112.227', 53823), ('183.13.205.157', 62348), ('168.1.24.73', 43848), ('144.132.44.65', 39426), ('125.209.92.221', 37986), ('112.141.58.167', 25914), ('112.10.23.147', 45241), ('104.5.169.41', 25308), ('92.237.15.59', 23965), ('88.16.150.152', 30098), ('82.24.138.43', 49740), ('82.12.72.167', 31238), ('62.150.79.73', 12959), ('46.182.186.120', 14830), ('41.186.83.139', 39809), ('41.139.250.38', 27948), ('41.80.228.149', 51315), ('37.142.3.43', 15902), ('197.248.146.186', 47562), ('196.202.169.186', 54056), ('195.89.120.114', 1887), ('190.58.8.8', 51356), ('178.80.20.88', 32719), ('154.73.87.85', 50759), ('128.106.160.157', 21075), ('120.244.110.7', 30692), ('109.190.212.96', 51413), ('103.244.242.125', 57601), ('103.53.73.37', 49569), ('92.99.64.137', 39323), ('86.83.69.149', 64478), ('86.18.224.70', 10087), ('82.23.110.144', 37364), ('79.183.99.166', 53756), ('79.67.57.240', 29678), ('43.246.200.117', 57837), ('43.241.145.68', 18244), ('41.90.111.158', 62329), ('24.203.151.158', 15655), ('196.202.169.186', 52047), ('165.231.40.14', 14529), ('162.246.107.146', 5250), ('113.20.98.46', 59247), ('105.232.88.134', 58042), ('103.70.204.194', 60803), ('96.43.189.107', 39391), ('82.7.117.162', 49036), ('66.87.207.99', 0), ('63.143.236.113', 0), ('41.210.10.227', 59850), ('31.45.103.138', 58745), ('222.184.232.173', 43972), ('216.151.184.159', 53348), ('208.103.233.162', 56253), ('196.209.233.164', 34498), ('196.207.184.51', 58026), ('195.147.181.190', 9979), ('156.222.46.220', 53780), ('154.70.57.66', 42107), ('112.205.157.246', 57546), ('105.184.153.117', 61483), ('103.123.36.181', 45900), ('90.219.115.171', 39294), ('81.100.255.223', 59631), ('80.39.134.201', 14986), ('77.68.243.70', 35000), ('72.229.37.71', 33869), ('49.146.13.126', 59667), ('41.79.66.225', 22121), ('221.203.22.245', 13235), ('186.150.134.175', 47674), ('178.166.33.36', 32733), ('124.107.192.43', 35453), ('123.245.20.142', 16726), ('121.54.56.38', 19377), ('116.90.96.10', 22968), ('108.4.215.251', 33971), ('104.207.83.98', 10899), ('96.23.181.91', 60231), ('86.137.21.83', 37329), ('75.65.215.196', 41839), ('63.245.58.155', 33565), ('46.209.44.99', 1302), ('182.186.253.171', 14647), ('178.157.249.62', 26603), ('130.105.164.123', 18359), ('105.232.86.49', 37295), ('103.252.202.150', 44822), ('103.4.248.138', 55841), ('95.94.157.175', 53805), ('94.175.148.12', 48143), ('94.76.15.44', 49920), ('89.237.98.87', 62852), ('87.70.101.199', 44609), ('86.25.124.158', 45263), ('72.230.218.136', 15580), ('70.65.227.21', 28936), ('41.139.243.58', 19483), ('41.60.233.245', 43611), ('31.215.14.8', 23218), ('1.9.135.44', 21209), ('203.212.155.83', 30754), ('202.142.91.102', 24654), ('197.237.129.150', 25308), ('197.232.26.118', 8999), ('197.232.19.110', 56174), ('196.249.103.196', 14869), ('187.112.124.166', 8999), ('183.87.169.155', 16437), ('117.194.220.81', 25839)]

Process finished with exit code 0
-]]> - - bitTorrent - protocol - p2p - -
- - BitTorrent协议(四)之bitfield消息 - /2019/01/21/2019-1-23-bt-4/ - bitfield消息

BEP3的peer message章节说明了几种消息类型。 -其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。

- - -
-

‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.

-
-

这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)

-
0b0000 0000 0000
-
-

如果下载了piece 0, 则值应该是

-
0b1000 0000 0000 
-
-

如果全部下载完成,则应该是

-
0b1111 1111 1100
-
-

一共是10个1,表示10个piece。

-

但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100么?

-

我们可以自己制作一个种子来验证一下。

-

本地验证

用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示

-

file-bt

-

该文件有110个片。

-

我们读取代码如下

-
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__author__ = 'ym'
"""
Date : '2019/1/22'
Description :

"""
from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend
from ym.bt.parse_torrent import BDecode
from ym.bt.udp_announce import ip_me
import socket
import struct
import logging
from bitstring import BitArray, Bits

log = logging.getLogger()


def download():
try:
info_hash = 'c99c3b7a5ba31a8966e6c9a40bc4f83887a107e5'
ip, port = ip_me(), 40959
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
sock.connect((ip, port))

handshake = pack_handshake(info_hash)
rt = sendall(sock, handshake)
if rt is None:
return
print("sent={}, data={}".format(rt, handshake))
data = recv(sock, 68)

tup = unpack_handshake(data)
if tup is None:
return
print(tup)

# interested
data = struct.pack(">i B", 1, 2)
rt = sendall(sock, data)
if rt is None:
return
bitfield = None
while True:
data = recv(sock, 4)
if data is None:
return
num, = struct.unpack(">i", data)
tmp = recv(sock, 1)

if tmp is None:
return
id, = struct.unpack(">B", tmp)
print("id={}".format(id))
tmp = recv(sock, int(num) - 1)
print("recv={}".format(tmp))
if id == 5:
# bitfield
bitfield = BitArray(tmp)
print(bitfield.bin)
break
else:
pass



except Exception as e:
log.exception(e)


if __name__ == '__main__':
download()


- -

我们看到日志如下

-
/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
-sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
-Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
-id=20
-recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
-id=5
-recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
-1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
+
  • ANNOUNCE,拿着connection_id和info_hash等参数再请求。
  • + +

    参考了Bittorrent udp-tracker protocol extension +因为对入参和返回参数说明的更详细一些。用的tracker是一个热门的磁力链接。

    +
    ip = 'exodus.desync.com'  # get from tracker list
    +port = 6969  # get from tracker list
     
    -

    这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?

    -
    111111111111111111111111011001110111111010101111101111110011110...
    +

    而info_hash是用uTorrent软件打开该磁力链接后获得的。

    +
    1aa4c13830b822c1375686d685a9fce23405f6ba
     
    -

    应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。

    -

    have消息

    原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息来 -补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。

    -

    因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index -置为1。

    -
    from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend
    from ym.bt.parse_torrent import BDecode
    from ym.bt.udp_announce import ip_me
    import socket
    import struct
    import logging
    from bitstring import BitArray, Bits

    log = logging.getLogger()


    def download():
    try:
    info_hash = 'c99c3b7a5ba31a8966e6c9a40bc4f83887a107e5'
    ip, port = ip_me(), 40959
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    sock.connect((ip, port))

    handshake = pack_handshake(info_hash)
    rt = sendall(sock, handshake)
    if rt is None:
    return
    print("sent={}, data={}".format(rt, handshake))
    data = recv(sock, 68)

    tup = unpack_handshake(data)
    if tup is None:
    return
    print(tup)

    # interested
    data = struct.pack(">i B", 1, 2)
    rt = sendall(sock, data)
    if rt is None:
    return
    bitfield = None
    while True:
    data = recv(sock, 4)
    if data is None:
    return
    num, = struct.unpack(">i", data)
    tmp = recv(sock, 1)

    if tmp is None:
    return
    id, = struct.unpack(">B", tmp)
    print("id={}".format(id))
    tmp = recv(sock, int(num) - 1)
    print("recv={}".format(tmp))
    if id == 5:
    # bitfield
    bitfield = BitArray(tmp)
    print(bitfield.bin)
    elif id == 4:
    # have
    index, = struct.unpack(">i", tmp)
    print("index={}".format(index))
    if bitfield.bin[index] == '0':
    bitfield.set('1', index)
    print(bitfield.bin)
    else:
    pass



    except Exception as e:
    log.exception(e)


    if __name__ == '__main__':
    download()
    - -

    我们再看一下日志

    -
    /usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
    -sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
    -Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
    -id=20
    -recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
    -id=5
    -recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
    -1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
    -id=4
    -recv=b'\x00\x00\x002'
    -index=50
    -1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
    -
    -...
    -...
    -...
    -
    -id=4
    -recv=b'\x00\x00\x00b'
    -index=98
    -1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
    -id=4
    -recv=b'\x00\x00\x00X'
    -index=88
    -1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
    -id=4
    -recv=b'\x00\x00\x00\n'
    -index=10
    -1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
    -already recv:b''
    -timed out
    -Traceback (most recent call last):
    -  File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
    -    tmp = sock.recv(n - len(data))
    -socket.timeout: timed out
    +

    以下是代码实现。uTorrent软件打开磁力链接会拿到tracker列表。

    +

    announce的重要参数说明

    +
      +
    1. num_want : -1 # 希望获得的peer个数,则是默认的peer个数
    2. +
    3. ip : 0 # 当前发送udp包的地址
    4. +
    5. port : 监听的公网端口
    6. +
    +

    一旦发布完成,该监听的端口会收到DHT协议的请求,例如FIND_NODE

    +
    # -*- coding: utf-8 -*-

    __author__ = 'ym'
    """
    Date : '2019/1/8'
    Description : udp tracker protocol

    """
    import socket
    import struct


    def ip_me():
    import psutil
    import socket

    def get_ip_addresses(family):
    for interface, snics in psutil.net_if_addrs().items():
    for snic in snics:
    if snic.family == family:
    yield (interface, snic.address)

    addrs = list(get_ip_addresses(socket.AF_INET))
    for tup in addrs:
    if tup[0] == 'en0':
    return tup[1]


    SOURCE_IP = ip_me()
    SOURCE_PORT = 59876


    def to_byte(integer, length=4):
    return integer.to_bytes(length, byteorder='big')


    def udp_send(ip, port, data):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
    sock.bind((SOURCE_IP, SOURCE_PORT))
    sock.sendto(data, (ip, port))


    def udp_recv(ip, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
    sock.bind((ip, port))
    while True:
    data, addr = sock.recvfrom(2048)
    print("received message:", data)
    return data


    class UdpAnnouceRequest(object):
    def __init__(self):
    self.connection_id = None
    self.action = None
    self.transaction_id = None
    self.info_hash = None
    self.peer_id = None
    self.downloaded = None
    self.left = None
    self.uploaded = None
    self.event = 0
    self.ip = None
    self.key = None
    self.num_want = -1
    self.port = None
    self.extensions = None

    def get_bytes(self):
    result = to_byte(self.connection_id, 8)
    result += to_byte(self.action)
    result += to_byte(self.transaction_id)
    result += self.info_hash
    result += self.peer_id
    result += to_byte(self.downloaded, 8)
    result += to_byte(self.left, 8)
    result += to_byte(self.uploaded, 8)
    result += to_byte(self.event)
    result += to_byte(self.ip)
    result += to_byte(self.key)
    result += struct.pack(">i", self.num_want)
    result += to_byte(self.port, 2)
    return result


    def udp_announcing(request: UdpAnnouceRequest, dst_ip, dst_port):
    data = request.get_bytes()
    udp_send(dst_ip, dst_port, data)


    def connect(tracker_ip, tracker_port):
    protocol_id = 0x41727101980 # magic number
    action = 0 # connect
    transaction_id = 123 # random

    data = struct.pack(">Qii", protocol_id, action, transaction_id)
    udp_send(tracker_ip, tracker_port, data)

    reply = udp_recv(SOURCE_IP, SOURCE_PORT)
    action, transaction_id, connection_id = struct.unpack(">iiQ", reply)
    fmt = "connect reply:\n\taction:{}, transaction_id:{}, connection_id:{}"
    print(fmt.format(action, transaction_id, connection_id))
    return connection_id


    def announce(connection_id, dst_ip, dst_port):
    request = UdpAnnouceRequest()
    request.connection_id = connection_id
    request.action = 1
    request.transaction_id = 1234 # random
    request.info_hash = bytearray.fromhex('1aa4c13830b822c1375686d685a9fce23405f6ba') # compute from meta_info
    request.peer_id = bytearray.fromhex('ab' * 20) # random
    request.downloaded = 0
    request.left = 1509949440 # from meta_info
    request.uploaded = 0
    request.ip = 0 # default
    request.key = 314
    request.port = 59601 #
    # print(request)
    udp_announcing(request, dst_ip, dst_port)
    return udp_recv(SOURCE_IP, SOURCE_PORT)


    def udp_process():
    ip = 'exodus.desync.com' # get from tracker list
    port = 6969 # get from tracker list
    connection_id = connect(ip, port)
    reply = announce(connection_id, ip, port)
    action, transaction_id, interval, leechers, seeders = struct.unpack(">iiiii", reply[0:20])
    fmt = "announce_reply:\n\taction:{}, transaction_id:{}, interval:{}s, leechers:{}, seeders:{}"
    print(fmt.format(action, transaction_id, interval, leechers, seeders))
    peers = []
    for i in range(20, len(reply), 6):
    if i + 6 <= len(reply):
    data = reply[i:i + 6]
    n1, n2, n3, n4, peer_port = struct.unpack(">BBBBH", data)
    peer_ip = "{}.{}.{}.{}".format(n1, n2, n3, n4)
    peers.append((peer_ip, peer_port))
    print("get {} peers:{}".format(len(peers), peers))


    if __name__ == '__main__':
    udp_process()
    -Process finished with exit code 0 -
    -

    可以看到,确实是110个1。

    +

    运行结果

    运行日志如下,可以看到拿到了200个peers的ipv4地址和端口。

    +
    /usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/udp_announce.py
    received message: b'\x00\x00\x00\x00\x00\x00\x00{\xd89\xa8\x9dv\xd3=]'
    connect reply:
    action:0, transaction_id:123, connection_id:15580669780121828701
    received message: b'\x00\x00\x00\x01\x00\x00\x04\xd2\x00\x00\x07_\x00\x00\x00%\x00\x00\x01qi\xe8\xff\xd8XWl\x18-F\xbcd\x9e\xae}\xea\xaa[\xd41S\'\xcb\x0eb\x14\x8f\xcfbcg\xcd\xaf9\xe0\x1f-\xf7*\xa6\xa2\x86)P\xadl\xbc\xbf)\xb6{\xc4w\xaa)\xcd\xf0@cF--;M\xa6Wp\xce\xc7\xff\xfc\xcf\xbb\x9e\x18\x9c\xec\x96\xc4\xcf\x83PS\x06_\xb4zmc\xd3\x82i\xef\x0b\xeb\xd0\xaf ~d\xf3\x8c\xde\x9b?\xfc\xdb!1\x93W\x0b\xc3I\xad\xf4,)\xf0\x1c\xc5\xe0\x98\x80\xbbZ\xb4^UA#\'|\x95\x7fd\xc77p\xc8Z\xadN\xb5p\xc7K\xbc\xd31i\x9c\xe0\x7f0\xaeV\x02\xc2\xe3G.R\x92\xfc\x15\xc8\x05R\x18\x18I\x943H3~\r\xd3\x89=\x06\xe6\xbb86-y\xd1\xa6\xca\xcb)\xd7\r\x8aLo)\xcb\xda\x8e;K)Z<\x9c\xb0\x87);Q\xd0\xb2\x8b\x18\xa5\xc2T\xb8\xd7\x05\x90;\x83\xf1^\xca\x8eeB\xa1a\xb9\xe8\x15z\x83\xde\x93\n\xba\x91+\x9em\xb1\x9b\xd0V\x82Y\xf2\xb5\xad\xc7\rV\x06\xf8^k\x14R\x8eP\xb1\xfb\x15Rf\x1eV\xd0\x05R-\xd9\xf5\xc2"@\x86\xf0K\x00\x00@B\xdeFa*\x1f;\x02<\xa2\xa7\x18l\xed\xfa\xd2\xf4\x05k#\x81\xe8\xa5\x01\xban\x82\xcd\t\xd57\xb8\x8eQ\xdd\xd4\x1b\x13\xfalh\xd0\x83\xa5J\xa9\xb5\xce\xe7x:\xb8\xe1\xc9\xab\xc6\xf9\xa3:\xc5\xed\xc5\xe4\xd2c\xb6\xb9\x1cL\xc0\x19\x8b<\xbfn\x9a\xc2\x80\xcc\xf3\xc3?\xefn\x8dg\xa5=\x0cm\xb1^\xfcU\xf3[}\xdd\x90\x82yS\xe9o\xf3\xf0R@w\xc0\xf4\'\xae\xd0SR\xcc\xde@\xc5\xf8\xdb\x9e\x8a\x0f\xb3\x08_\x90\xb4\xc1\xa9=\xda$\x1a\xe1n\xe8V\r\xe5\xe1b\x8cr{1\xc3^\x05\xe8bR\x00Sn\xe5\x7fe\xe5G4V5I \x1bjQ\x9a\x80\x7f\xce\xe7y\xd5\xde]\xbc\x81D\xbd\xbf\xf1\xb8K\xdfZ\x8d\xe5\xa9=\xda#\x1a\xe1sE\xbb\xb8\xd4Oi\xa7\x10V\xe9\xffgG\x10\xf3\x8f\xde^\xea#S+%\\\xeeN_\xba\xbd3\x07g\xaf\xdb\xb3)\xa1:#\xe0G%\x98\xce\x84}\xe2\xd8ch\xeaN\xb2\xbe\xabp\xe3\xd2?\xb7\r\xcd\x9d\xf3\x8c\xa8\x01\x18I\xabH\x90\x84,A\x9a\x02}\xd1\\\xdd\x94bp\x8d:\xa7e:p\n\x17\x93\xb0\xb9h\x05\xa9)b\xdc\\\xed\x0f;]\x9dX\x10\x96\x98u\x92R\x18\x8a+\xc2LR\x0cH\xa7z\x06>\x96OI2\x9f.\xb6\xbax9\xee)\xbaS\x8b\x9b\x81)\x8b\xfa&m,)P\xe4\x95\xc8s%\x8e\x03+>\x1e\xc5\xf8\x92\xba\xb9\xca\xc4\xca\xa9\xba\xd3(\xc3Yxr\x07_\xbe:\x08\x08\xc8\x9c\xb2P\x14X\x7f\xcf\x9aIWU\xc6G\x80j\xa0\x9dRSx\xf4n\x07w\xe4m\xbe\xd4`\xc8\xd5g\xf4\xf2}\xe1\x01g5I%\xc1\xa1\\c@\x89\x99\x9bVSE\x95\xfb\xdeV\x12\xe0F\'gR\x17n\x90\x91\xf4O\xb7c\xa6\xd1\xfcOC9\xf0s\xee+\xf6\xc8u\xe1\xed+\xf1\x91DGD)Zo\x9e\xf3y\x18\xcb\x97\x9e=\'\xc4\xca\xa9\xba\xcbO\xa5\xe7(\x0e8\xc1\xa2\xf6k\x92\x14\x82q\x14b.\xe7oi\xe8X\x86\xe2\xbagF\xcc\xc2\xed\x83`+\xbdk\x99\xdfR\x07u\xa2\xbf\x8cBW\xcfc\x00\x00?\x8f\xecq\x00\x00)\xd2\n\xe3\xe9\xca\x1f-g\x8a\xe5y\xde\xb8\xe8\xad\xab\xc4\xd8\x97\xb8\x9f\xd0d\xd0g\xe9\xa2\xdb\xbd\xc4\xd1\xe9\xa4\x86\xc2\xc4\xcf\xb83\xe2\xaa\xc3\x93\xb5\xbe&\xfb\x9c\xde.\xdc\xd2\x14\x9aF9B\xa4{p\xcd\x9d\xf6\xe0\xcai\xb8\x99u\xf0+g{$\xb5\xb3LZ\xdbs\xab\x99~Qd\xff\xdf\xe8\xefP\'\x86\xc9:\x8aMD\xf3F\x88\xb8H\xe5%G\x84M1\x92\r~\xe9\x13)OB\xe1Vi\xdd\xcb\x16\xf53\xb3\xba\x96\x86\xaf\xba:\xb2\xa6!$\x7f\xdd|k\xc0+\x8a}{\xf5\x14\x8eAVy68&K\xb1tZ`\nY\xb8l\x04\xd7\xfb\x84\xb3h\xcfSb*\x93`\x17\xb5[\xebGV\x89\x15S\x91\xd1KA\xd7\xc4\xa3o?\xf5:\x9b\x83\x1d.\xd1,c\x05\x16\xb6\xba\xfd\xab97\xb2\x9d\xf9>g\xeb\x82i\xa4{G\xb7i\xe8V1\x91\xafg\xfc\xca\x96\xaf\x16g\x04\xf8\x8a\xda!_^\x9d\xaf\xd2-^\xaf\x94\x0c\xbc\x0f^L\x0f,\xc3\x00Y\xedbW\xf5\x84WFe\xc7\xaeAV\x19|\x9e\xb0\xcfH\xe6\xda\x88<\xdcFA\xe3\x15q\x08)\x8b\xf3:L\x1b)<\xe9\xf5\xaa[\x1f\xd7\x0e\x08Z\xb2\x01\t\x87,R\xd9\xcb\xd4\x9bSx"\xca\x8e[f`N\xc5\xed\x81\x96b\xdc\xc5\xe8\x1av#\'\xc5\xe8\x13n\xdbn\xc4\xf9g\xc4:\x15\xbbp|\xa6#\'\xb7W\xa9\x9b@5u\xc2\xdcQd\xef'
    announce_reply:
    action:1, transaction_id:1234, interval:1887s, leechers:37, seeders:369
    get 200 peers:[('105.232.255.216', 22615), ('108.24.45.70', 48228), ('158.174.125.234', 43611), ('212.49.83.39', 51982), ('98.20.143.207', 25187), ('103.205.175.57', 57375), ('45.247.42.166', 41606), ('41.80.173.108', 48319), ('41.182.123.196', 30634), ('41.205.240.64', 25414), ('45.45.59.77', 42583), ('112.206.199.255', 64719), ('187.158.24.156', 60566), ('196.207.131.80', 21254), ('95.180.122.109', 25555), ('130.105.239.11', 60368), ('175.32.126.100', 62348), ('222.155.63.252', 56097), ('49.147.87.11', 49993), ('173.244.44.41', 61468), ('197.224.152.128', 47962), ('180.94.85.65', 8999), ('124.149.127.100', 50999), ('112.200.90.173', 20149), ('112.199.75.188', 54065), ('105.156.224.127', 12462), ('86.2.194.227', 18222), ('82.146.252.21', 51205), ('82.24.24.73', 37939), ('72.51.126.13', 54153), ('61.6.230.187', 14390), ('45.121.209.166', 51915), ('41.215.13.138', 19567), ('41.203.218.142', 15179), ('41.90.60.156', 45191), ('41.59.81.208', 45707), ('24.165.194.84', 47319), ('5.144.59.131', 61790), ('202.142.101.66', 41313), ('185.232.21.122', 33758), ('147.10.186.145', 11166), ('109.177.155.208', 22146), ('89.242.181.173', 50957), ('86.6.248.94', 27412), ('82.142.80.177', 64277), ('82.102.30.86', 53253), ('82.45.217.245', 49698), ('64.134.240.75', 0), ('64.66.222.70', 24874), ('31.59.2.60', 41639), ('24.108.237.250', 54004), ('5.107.35.129', 59557), ('1.186.110.130', 52489), ('213.55.184.142', 20957), ('212.27.19.250', 27752), ('208.131.165.74', 43445), ('206.231.120.58', 47329), ('201.171.198.249', 41786), ('197.237.197.228', 53859), ('182.185.28.76', 49177), ('139.60.191.110', 39618), ('128.204.243.195', 16367), ('110.141.103.165', 15628), ('109.177.94.252', 22003), ('91.125.221.144', 33401), ('83.233.111.243', 61522), ('64.119.192.244', 10158), ('208.83.82.204', 56896), ('197.248.219.158', 35343), ('179.8.95.144', 46273), ('169.61.218.36', 6881), ('110.232.86.13', 58849), ('98.140.114.123', 12739), ('94.5.232.98', 20992), ('83.110.229.127', 26085), ('71.52.86.53', 18720), ('27.106.81.154', 32895), ('206.231.121.213', 56925), ('188.129.68.189', 49137), ('184.75.223.90', 36325), ('169.61.218.35', 6881), ('115.69.187.184', 54351), ('105.167.16.86', 59903), ('103.71.16.243', 36830), ('94.234.35.83', 11045), ('92.238.78.95', 47805), ('51.7.103.175', 56243), ('41.161.58.35', 57415), ('37.152.206.132', 32226), ('216.99.104.234', 20146), ('190.171.112.227', 53823), ('183.13.205.157', 62348), ('168.1.24.73', 43848), ('144.132.44.65', 39426), ('125.209.92.221', 37986), ('112.141.58.167', 25914), ('112.10.23.147', 45241), ('104.5.169.41', 25308), ('92.237.15.59', 23965), ('88.16.150.152', 30098), ('82.24.138.43', 49740), ('82.12.72.167', 31238), ('62.150.79.73', 12959), ('46.182.186.120', 14830), ('41.186.83.139', 39809), ('41.139.250.38', 27948), ('41.80.228.149', 51315), ('37.142.3.43', 15902), ('197.248.146.186', 47562), ('196.202.169.186', 54056), ('195.89.120.114', 1887), ('190.58.8.8', 51356), ('178.80.20.88', 32719), ('154.73.87.85', 50759), ('128.106.160.157', 21075), ('120.244.110.7', 30692), ('109.190.212.96', 51413), ('103.244.242.125', 57601), ('103.53.73.37', 49569), ('92.99.64.137', 39323), ('86.83.69.149', 64478), ('86.18.224.70', 10087), ('82.23.110.144', 37364), ('79.183.99.166', 53756), ('79.67.57.240', 29678), ('43.246.200.117', 57837), ('43.241.145.68', 18244), ('41.90.111.158', 62329), ('24.203.151.158', 15655), ('196.202.169.186', 52047), ('165.231.40.14', 14529), ('162.246.107.146', 5250), ('113.20.98.46', 59247), ('105.232.88.134', 58042), ('103.70.204.194', 60803), ('96.43.189.107', 39391), ('82.7.117.162', 49036), ('66.87.207.99', 0), ('63.143.236.113', 0), ('41.210.10.227', 59850), ('31.45.103.138', 58745), ('222.184.232.173', 43972), ('216.151.184.159', 53348), ('208.103.233.162', 56253), ('196.209.233.164', 34498), ('196.207.184.51', 58026), ('195.147.181.190', 9979), ('156.222.46.220', 53780), ('154.70.57.66', 42107), ('112.205.157.246', 57546), ('105.184.153.117', 61483), ('103.123.36.181', 45900), ('90.219.115.171', 39294), ('81.100.255.223', 59631), ('80.39.134.201', 14986), ('77.68.243.70', 35000), ('72.229.37.71', 33869), ('49.146.13.126', 59667), ('41.79.66.225', 22121), ('221.203.22.245', 13235), ('186.150.134.175', 47674), ('178.166.33.36', 32733), ('124.107.192.43', 35453), ('123.245.20.142', 16726), ('121.54.56.38', 19377), ('116.90.96.10', 22968), ('108.4.215.251', 33971), ('104.207.83.98', 10899), ('96.23.181.91', 60231), ('86.137.21.83', 37329), ('75.65.215.196', 41839), ('63.245.58.155', 33565), ('46.209.44.99', 1302), ('182.186.253.171', 14647), ('178.157.249.62', 26603), ('130.105.164.123', 18359), ('105.232.86.49', 37295), ('103.252.202.150', 44822), ('103.4.248.138', 55841), ('95.94.157.175', 53805), ('94.175.148.12', 48143), ('94.76.15.44', 49920), ('89.237.98.87', 62852), ('87.70.101.199', 44609), ('86.25.124.158', 45263), ('72.230.218.136', 15580), ('70.65.227.21', 28936), ('41.139.243.58', 19483), ('41.60.233.245', 43611), ('31.215.14.8', 23218), ('1.9.135.44', 21209), ('203.212.155.83', 30754), ('202.142.91.102', 24654), ('197.237.129.150', 25308), ('197.232.26.118', 8999), ('197.232.19.110', 56174), ('196.249.103.196', 14869), ('187.112.124.166', 8999), ('183.87.169.155', 16437), ('117.194.220.81', 25839)]

    Process finished with exit code 0
    ]]> bitTorrent @@ -2004,6 +1909,101 @@ download ok. Process finished with exit code 0
    +]]> + + bitTorrent + protocol + p2p + + + + BitTorrent协议(四)之bitfield消息 + /2019/01/21/2019-1-23-bt-4/ + bitfield消息

    BEP3的peer message章节说明了几种消息类型。 +其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。

    + + +
    +

    ‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.

    +
    +

    这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)

    +
    0b0000 0000 0000
    +
    +

    如果下载了piece 0, 则值应该是

    +
    0b1000 0000 0000 
    +
    +

    如果全部下载完成,则应该是

    +
    0b1111 1111 1100
    +
    +

    一共是10个1,表示10个piece。

    +

    但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100么?

    +

    我们可以自己制作一个种子来验证一下。

    +

    本地验证

    用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示

    +

    file-bt

    +

    该文件有110个片。

    +

    我们读取代码如下

    +
    # -*- coding: utf-8 -*-
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function

    __author__ = 'ym'
    """
    Date : '2019/1/22'
    Description :

    """
    from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend
    from ym.bt.parse_torrent import BDecode
    from ym.bt.udp_announce import ip_me
    import socket
    import struct
    import logging
    from bitstring import BitArray, Bits

    log = logging.getLogger()


    def download():
    try:
    info_hash = 'c99c3b7a5ba31a8966e6c9a40bc4f83887a107e5'
    ip, port = ip_me(), 40959
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    sock.connect((ip, port))

    handshake = pack_handshake(info_hash)
    rt = sendall(sock, handshake)
    if rt is None:
    return
    print("sent={}, data={}".format(rt, handshake))
    data = recv(sock, 68)

    tup = unpack_handshake(data)
    if tup is None:
    return
    print(tup)

    # interested
    data = struct.pack(">i B", 1, 2)
    rt = sendall(sock, data)
    if rt is None:
    return
    bitfield = None
    while True:
    data = recv(sock, 4)
    if data is None:
    return
    num, = struct.unpack(">i", data)
    tmp = recv(sock, 1)

    if tmp is None:
    return
    id, = struct.unpack(">B", tmp)
    print("id={}".format(id))
    tmp = recv(sock, int(num) - 1)
    print("recv={}".format(tmp))
    if id == 5:
    # bitfield
    bitfield = BitArray(tmp)
    print(bitfield.bin)
    break
    else:
    pass



    except Exception as e:
    log.exception(e)


    if __name__ == '__main__':
    download()


    + +

    我们看到日志如下

    +
    /usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
    +sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
    +Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
    +id=20
    +recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
    +id=5
    +recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
    +1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
    +
    +

    这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?

    +
    111111111111111111111111011001110111111010101111101111110011110...
    +
    +

    应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。

    +

    have消息

    原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息来 +补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。

    +

    因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index +置为1。

    +
    from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend
    from ym.bt.parse_torrent import BDecode
    from ym.bt.udp_announce import ip_me
    import socket
    import struct
    import logging
    from bitstring import BitArray, Bits

    log = logging.getLogger()


    def download():
    try:
    info_hash = 'c99c3b7a5ba31a8966e6c9a40bc4f83887a107e5'
    ip, port = ip_me(), 40959
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    sock.connect((ip, port))

    handshake = pack_handshake(info_hash)
    rt = sendall(sock, handshake)
    if rt is None:
    return
    print("sent={}, data={}".format(rt, handshake))
    data = recv(sock, 68)

    tup = unpack_handshake(data)
    if tup is None:
    return
    print(tup)

    # interested
    data = struct.pack(">i B", 1, 2)
    rt = sendall(sock, data)
    if rt is None:
    return
    bitfield = None
    while True:
    data = recv(sock, 4)
    if data is None:
    return
    num, = struct.unpack(">i", data)
    tmp = recv(sock, 1)

    if tmp is None:
    return
    id, = struct.unpack(">B", tmp)
    print("id={}".format(id))
    tmp = recv(sock, int(num) - 1)
    print("recv={}".format(tmp))
    if id == 5:
    # bitfield
    bitfield = BitArray(tmp)
    print(bitfield.bin)
    elif id == 4:
    # have
    index, = struct.unpack(">i", tmp)
    print("index={}".format(index))
    if bitfield.bin[index] == '0':
    bitfield.set('1', index)
    print(bitfield.bin)
    else:
    pass



    except Exception as e:
    log.exception(e)


    if __name__ == '__main__':
    download()
    + +

    我们再看一下日志

    +
    /usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
    +sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
    +Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
    +id=20
    +recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
    +id=5
    +recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
    +1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
    +id=4
    +recv=b'\x00\x00\x002'
    +index=50
    +1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
    +
    +...
    +...
    +...
    +
    +id=4
    +recv=b'\x00\x00\x00b'
    +index=98
    +1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
    +id=4
    +recv=b'\x00\x00\x00X'
    +index=88
    +1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
    +id=4
    +recv=b'\x00\x00\x00\n'
    +index=10
    +1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
    +already recv:b''
    +timed out
    +Traceback (most recent call last):
    +  File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
    +    tmp = sock.recv(n - len(data))
    +socket.timeout: timed out
    +
    +Process finished with exit code 0
    +
    +

    可以看到,确实是110个1。

    ]]>
    bitTorrent @@ -2350,54 +2350,6 @@ csn为UTF-8

    file
    - - 基于CLion和gdbserver实现远程调试c程序 - /2020/09/08/remote-debug-with-clion/ - 远程调试c程序

    最近基于tsar(阿里开源的一个基于c语言的监控程序)做二次开发, -因为以前从来没有在工作中写过c,所以这个简单的工作花了两周时间,期间用gdb进行调试,用valgrind检查内存泄漏。 -但是最让我不舒服的就是gdb调试了,虽然gdb很给力,但是毕竟由奢入俭难,之前写Java,Python,Go都是可以用IDE进行 -debug的。有图形化界面还是效率高很多,而对于新手,能够方便的debug源码就可以快速的理解项目。

    -

    那么怎么办呢?

    - - - -

    CLion

    我一直以为CLion可以很好的理解cmake项目,但是对于大量的基于makefile编译的项目,则不能很好的解析,所以也无法利用CLion -进行代码调试。

    -

    但是并不是这样,虽然CLion无法理解代码中各种符号之间的依赖关系,但是调试只要有debug info和源码就可以进行图形化调试。

    -

    参考CLion远程开发的Remote GDB Server,很简单就实现了。

    -

    远程调试tsar

      -
    1. mac下安装了CLion,linux下编译tsar。 -在mac下,tsar项目源码路径——/Users/ym/work/operation -在linux下为,tsar项目源码路径——/home/keyvalue/ym/operation

      -
    2. -
    3. 在CLion下创建一个GDB remote debug配置。 -参数配置:

      -
    4. -
    -
      -
    • target remote args
      10.4.104.153:1234 # 这是gdbserver的ip:port

      -
    • -
    • path mappings -remote path为/home/keyvalue/ym/operation,local path为/Users/ym/work/operation

      -
    • -
    -

    gdb remote debug config

    -
      -
    1. 在linux下运行命令

      -
      $ sudo gdbserver :1234 src/tsar --cron
      Process src/tsar created; pid = 10216
      Listening on port 1234
      -
    2. -
    3. CLion下启动之前的GDB remote debug配置,就可以愉快的断点调试了。

      -
    4. -
    -

    tsar remote debug

    -]]>
    - - debug - CLion - gdb - remote - -
    自建根证书,中间证书和Server端X.509证书并搭建nginx验证Server端证书有效性 /2020/10/09/x509-ca/ @@ -2445,11 +2397,59 @@ remote path为/home/keyvalue/ym/operation,local path为/Use

    valid-crt

    ]]> - certificate - X.509 - nginx - openssl - ssl + certificate + X.509 + nginx + openssl + ssl + +
    + + 基于CLion和gdbserver实现远程调试c程序 + /2020/09/08/remote-debug-with-clion/ + 远程调试c程序

    最近基于tsar(阿里开源的一个基于c语言的监控程序)做二次开发, +因为以前从来没有在工作中写过c,所以这个简单的工作花了两周时间,期间用gdb进行调试,用valgrind检查内存泄漏。 +但是最让我不舒服的就是gdb调试了,虽然gdb很给力,但是毕竟由奢入俭难,之前写Java,Python,Go都是可以用IDE进行 +debug的。有图形化界面还是效率高很多,而对于新手,能够方便的debug源码就可以快速的理解项目。

    +

    那么怎么办呢?

    + + + +

    CLion

    我一直以为CLion可以很好的理解cmake项目,但是对于大量的基于makefile编译的项目,则不能很好的解析,所以也无法利用CLion +进行代码调试。

    +

    但是并不是这样,虽然CLion无法理解代码中各种符号之间的依赖关系,但是调试只要有debug info和源码就可以进行图形化调试。

    +

    参考CLion远程开发的Remote GDB Server,很简单就实现了。

    +

    远程调试tsar

      +
    1. mac下安装了CLion,linux下编译tsar。 +在mac下,tsar项目源码路径——/Users/ym/work/operation +在linux下为,tsar项目源码路径——/home/keyvalue/ym/operation

      +
    2. +
    3. 在CLion下创建一个GDB remote debug配置。 +参数配置:

      +
    4. +
    +
      +
    • target remote args
      10.4.104.153:1234 # 这是gdbserver的ip:port

      +
    • +
    • path mappings +remote path为/home/keyvalue/ym/operation,local path为/Users/ym/work/operation

      +
    • +
    +

    gdb remote debug config

    +
      +
    1. 在linux下运行命令

      +
      $ sudo gdbserver :1234 src/tsar --cron
      Process src/tsar created; pid = 10216
      Listening on port 1234
      +
    2. +
    3. CLion下启动之前的GDB remote debug配置,就可以愉快的断点调试了。

      +
    4. +
    +

    tsar remote debug

    +]]>
    + + debug + CLion + gdb + remote
    @@ -2570,6 +2570,66 @@ byte & 0x7F 表示length的编码长度。 ASN.1 + + CLion2021调试Makefile项目 + /2021/07/15/2021-7-15-clion-makefile-debug/ + CLion介绍

    CLion是一款针对C/C++项目的跨平台的集成IDE(A cross-platform IDE for C and C++)。2020版本以前,只支持cmake项目, +但是2021版本对Makefile项目的支持度增加了。我们看看如何对Makefile项目进行断点调试。

    + + +

    我们用一个实际的项目作为例子。

    +

    Go1.4源码

    Go语言项目从源码编译有几种方式,其中一种方式是基于Bootstrap toolchain from C source code, +也就是说,首先编译Go 1.4版本,然后用编译出来的Go,编译最新的Go版本。(UPDATE: Mac12.1 Monterey系统上不支持)

    +

    那么我下载这个Go1.4版本,go1.4-bootstrap-20171003.tar.gz, +解压到某个路径下。

    +
    ~/work/go1.4/ ls          
    AUTHORS LICENSE README api doc include misc robots.txt test
    CONTRIBUTORS PATENTS VERSION bin favicon.ico lib pkg src
    + +

    进入src路径下,运行make.bash文件,则开始编译。(当然需要build相关的工具)。为了看清楚bash脚本执行的内容,我们修改make.bash的 +第一行改为 set -ex, 这样会打印详细的执行内容如下,

    +
    g1.4/src/ $  ./make.bash 
    + '[' '!' -f run.bash ']'
    + case "$(uname)" in
    ++ uname
    + ld --version
    + grep 'gold.* 2\.20'
    + for se_mount in /selinux /sys/fs/selinux
    + '[' -d /selinux -a -f /selinux/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ']'
    + for se_mount in /selinux /sys/fs/selinux
    + '[' -d /sys/fs/selinux -a -f /sys/fs/selinux/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ']'
    ++ uname -s
    + '[' Darwin == GNU/kFreeBSD ']'
    + rm -f ./runtime/runtime_defs.go
    + echo '# Building C bootstrap tool.'
    # Building C bootstrap tool.
    + echo cmd/dist
    cmd/dist
    ++ cd ..
    ++ pwd
    + export GOROOT=/Users/ym/work/go1.4
    + GOROOT=/Users/ym/work/go1.4
    + GOROOT_FINAL=/Users/ym/work/go1.4
    + DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    + mflag=
    + case "$GOHOSTARCH" in
    ++ uname
    + '[' Darwin == Darwin ']'
    + mflag=' -mmacosx-version-min=10.6'
    ++ type -t gcc
    ++ type -t clang
    + '[' -z '' -a -z file -a -n file ']'
    + gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c
    ++ ./cmd/dist/dist env -p
    + eval 'CC="clang"' 'CC_FOR_TARGET="clang"' 'GOROOT="/Users/ym/work/go1.4"' 'GOBIN="/Users/ym/me/go/test/bin"' 'GOARCH="amd64"' 'GOOS="darwin"' 'GOHOSTARCH="amd64"' 'GOHOSTOS="darwin"' 'GOTOOLDIR="/Users/ym/work/go1.4/pkg/tool/darwin_amd64"' 'GOCHAR="6"' 'PATH="/Users/ym/me/go/test/bin:/usr/local/opt/openssl@1.1/bin:/usr/local/opt/postgresql@12/bin:/Users/ym/work/go1.4/bin:/usr/local/Homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/ym/mybin/apache-maven-3.6.0/bin:/Users/ym/me/go/test/bin:/Users/ym/.rvm/bin:/Users/ym/mybin/spark-2.4.3-bin-hadoop2.7/bin/"'
    ++ CC=clang
    ++ CC_FOR_TARGET=clang
    ++ GOROOT=/Users/ym/work/go1.4
    ++ GOBIN=/Users/ym/me/go/test/bin
    ++ GOARCH=amd64
    ++ GOOS=darwin
    ++ GOHOSTARCH=amd64
    ++ GOHOSTOS=darwin
    ++ GOTOOLDIR=/Users/ym/work/go1.4/pkg/tool/darwin_amd64
    ++ GOCHAR=6
    ++ PATH='/Users/ym/me/go/test/bin:/usr/local/opt/openssl@1.1/bin:/usr/local/opt/postgresql@12/bin:/Users/ym/work/go1.4/bin:/usr/local/Homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/ym/mybin/apache-maven-3.6.0/bin:/Users/ym/me/go/test/bin:/Users/ym/.rvm/bin:/Users/ym/mybin/spark-2.4.3-bin-hadoop2.7/bin/'
    + '[' '' = true ']'
    + echo

    + '[' '' = --dist-tool ']'
    + echo '# Building compilers and Go bootstrap tool for host, darwin/amd64.'
    # Building compilers and Go bootstrap tool for host, darwin/amd64.
    + buildall=-a
    + '[' '' = --no-clean ']'
    + ./cmd/dist/dist bootstrap -a -v
    lib9
    libbio
    liblink
    cmd/cc
    cmd/gc
    cmd/6l
    /Users/ym/work/go1.4/src/cmd/6l/../ld/dwarf.c:1479:15: warning: implicit conversion from 'int' to 'char' changes value from 156 to -100 [-Wconstant-conversion]
    /Users/ym/work/go1.4/src/cmd/6l/../ld/dwarf.c:1763:21: warning: implicit conversion from 'int' to 'char' changes value from 144 to -112 [-Wconstant-conversion]
    /Users/ym/work/go1.4/src/cmd/6l/../ld/lib.h:168:13: note: expanded from macro 'cput'
    cmd/6a
    cmd/6c
    /Users/ym/work/go1.4/src/cmd/6c/txt.c:995:28: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
    /Users/ym/work/go1.4/src/cmd/6c/txt.c:1045:28: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
    cmd/6g
    /Users/ym/work/go1.4/src/cmd/6g/peep.c:771:13: warning: converting the enum constant to a boolean [-Wint-in-bool-context]
    runtime
    errors
    sync/atomic
    sync
    io
    unicode
    unicode/utf8
    unicode/utf16
    bytes
    math
    strings
    strconv
    bufio
    sort
    container/heap
    encoding/base64
    syscall
    time
    os
    reflect
    fmt
    encoding
    encoding/json
    flag
    path/filepath
    path
    io/ioutil
    log
    regexp/syntax
    regexp
    go/token
    go/scanner
    go/ast
    go/parser
    os/exec
    os/signal
    net/url
    text/template/parse
    text/template
    go/doc
    go/build
    cmd/go
    + cp cmd/dist/dist /Users/ym/work/go1.4/pkg/tool/darwin_amd64/dist
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap clean -i std
    + echo

    + '[' amd64 '!=' amd64 -o darwin '!=' darwin ']'
    + echo '# Building packages and commands for darwin/amd64.'
    # Building packages and commands for darwin/amd64.
    + CC=clang
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap install -ccflags '' -gcflags '' -ldflags '' -v std
    runtime
    errors
    sync/atomic
    unicode
    unicode/utf8
    math
    sort
    encoding
    unicode/utf16
    container/list
    sync
    crypto/subtle
    container/ring
    image/color
    runtime/race
    container/heap
    io
    syscall
    image/color/palette
    hash
    crypto/cipher
    hash/crc32
    crypto/hmac
    hash/adler32
    hash/crc64
    hash/fnv
    bytes
    strings
    bufio
    text/tabwriter
    path
    html
    compress/bzip2
    time
    strconv
    math/rand
    math/cmplx
    os
    reflect
    regexp/syntax
    crypto
    encoding/base64
    net/url
    crypto/aes
    crypto/rc4
    crypto/md5
    crypto/sha1
    crypto/sha256
    crypto/sha512
    encoding/pem
    encoding/ascii85
    encoding/base32
    image
    path/filepath
    net
    os/signal
    io/ioutil
    os/exec
    regexp
    image/draw
    image/jpeg
    fmt
    encoding/binary
    cmd/pprof/internal/svg
    crypto/des
    index/suffixarray
    cmd/internal/goobj
    cmd/internal/rsc.io/arm/armasm
    cmd/internal/rsc.io/x86/x86asm
    debug/dwarf
    debug/gosym
    debug/plan9obj
    flag
    log
    go/token
    encoding/json
    encoding/xml
    text/template/parse
    go/scanner
    debug/elf
    debug/macho
    debug/pe
    go/ast
    compress/flate
    text/template
    math/big
    encoding/hex
    mime
    net/textproto
    cmd/internal/objfile
    net/http/internal
    compress/gzip
    runtime/pprof
    cmd/pack
    cmd/pprof/internal/profile
    cmd/pprof/internal/tempfile
    archive/tar
    archive/zip
    cmd/addr2line
    cmd/nm
    crypto/elliptic
    encoding/asn1
    crypto/rand
    go/parser
    go/printer
    go/doc
    crypto/ecdsa
    crypto/rsa
    crypto/dsa
    crypto/x509/pkix
    mime/multipart
    cmd/objdump
    cmd/pprof/internal/plugin
    crypto/x509
    html/template
    go/build
    cmd/cgo
    go/format
    cmd/fix
    cmd/gofmt
    crypto/tls
    cmd/pprof/internal/symbolizer
    cmd/pprof/internal/symbolz
    cmd/pprof/internal/report
    cmd/yacc
    compress/lzw
    compress/zlib
    database/sql/driver
    database/sql
    encoding/csv
    encoding/gob
    cmd/pprof/internal/commands
    image/gif
    cmd/pprof/internal/driver
    image/png
    log/syslog
    net/mail
    os/user
    runtime/debug
    testing
    testing/iotest
    net/http
    net/smtp
    testing/quick
    text/scanner
    cmd/go
    cmd/pprof/internal/fetch
    expvar
    net/http/cgi
    net/http/cookiejar
    net/http/httptest
    net/http/httputil
    net/http/pprof
    cmd/pprof
    net/rpc
    net/http/fcgi
    net/rpc/jsonrpc
    + echo

    + rm -f /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap
    + '[' '' '!=' --no-banner ']'
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/dist banner

    ---
    Installed Go for darwin/amd64 in /Users/ym/work/go1.4
    Installed commands in /Users/ym/me/go/test/bin
    + +

    我们看到15-33行, 首先编译了cmd/dist文件夹下的c代码,生成了一个dist可执行文件,并调用了 /cmd/dist/dist env -p

    +
    +# Building C bootstrap tool.
    ++ echo cmd/dist
    +cmd/dist
    +++ cd ..
    +++ pwd
    ++ export GOROOT=/Users/ym/work/go1.4
    ++ GOROOT=/Users/ym/work/go1.4
    ++ GOROOT_FINAL=/Users/ym/work/go1.4
    ++ DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    ++ mflag=
    ++ case "$GOHOSTARCH" in
    +++ uname
    ++ '[' Darwin == Darwin ']'
    ++ mflag=' -mmacosx-version-min=10.6'
    +++ type -t gcc
    +++ type -t clang
    ++ '[' -z '' -a -z file -a -n file ']'
    ++ gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c 
    +++ ./cmd/dist/dist env -p
    +
    + +

    那么我们来看看怎么在CLion中调试/cmd/dist/dist env -p

    +

    CLion调试cmd/dist

    如果要用CLion直接打开cmd/dist文件夹,会提示创建cmake项目,但是我们想直接使用调试Makefile项目。 +那么我们在cmd/dist目录下创建一个Makefile文件,内容如下

    +
    CFLAGS = -mmacosx-version-min=10.6 -g -Wall -Werror '-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    CC = gcc

    SRC=arm.c buf.c build.c buildgc.c buildgo.c buildruntime.c main.c plan9.c unix.c windows.c
    INCLUDE_DIR = ./


    TARGET = dist

    all: $(TARGET)

    $(TARGET):
    $(CC) $(CFLAGS) -I$(INCLUDE_DIR) $(SRC) -o $@


    clean:
    rm $(TARGET)

    .PHONY: all clean
    + +

    其实就是根据make.bash打印的gcc编译命令改写的。需要注意的是要增加-g选项,这样才能调试。

    +

    然后参考Run/Debug Configuration: Makefile Application

    +

    配置几个参数,核心要指定的就是Target和Executable文件 +clion-debug-makefile-config

    +

    最后先clean,然后断点调试unix.c文件中的main入口函数。

    +

    clion-makefile-debug-show

    +

    成功了。

    +]]>
    + + debug + clion + makefile + +
    k3s的Pod无法解析内网域名 /2021/12/26/pod-resolve-dn/ @@ -2709,66 +2769,6 @@ search addom.xinaogroup.com coredns - - CLion2021调试Makefile项目 - /2021/07/15/2021-7-15-clion-makefile-debug/ - CLion介绍

    CLion是一款针对C/C++项目的跨平台的集成IDE(A cross-platform IDE for C and C++)。2020版本以前,只支持cmake项目, -但是2021版本对Makefile项目的支持度增加了。我们看看如何对Makefile项目进行断点调试。

    - - -

    我们用一个实际的项目作为例子。

    -

    Go1.4源码

    Go语言项目从源码编译有几种方式,其中一种方式是基于Bootstrap toolchain from C source code, -也就是说,首先编译Go 1.4版本,然后用编译出来的Go,编译最新的Go版本。(UPDATE: Mac12.1 Monterey系统上不支持)

    -

    那么我下载这个Go1.4版本,go1.4-bootstrap-20171003.tar.gz, -解压到某个路径下。

    -
    ~/work/go1.4/ ls          
    AUTHORS LICENSE README api doc include misc robots.txt test
    CONTRIBUTORS PATENTS VERSION bin favicon.ico lib pkg src
    - -

    进入src路径下,运行make.bash文件,则开始编译。(当然需要build相关的工具)。为了看清楚bash脚本执行的内容,我们修改make.bash的 -第一行改为 set -ex, 这样会打印详细的执行内容如下,

    -
    g1.4/src/ $  ./make.bash 
    + '[' '!' -f run.bash ']'
    + case "$(uname)" in
    ++ uname
    + ld --version
    + grep 'gold.* 2\.20'
    + for se_mount in /selinux /sys/fs/selinux
    + '[' -d /selinux -a -f /selinux/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ']'
    + for se_mount in /selinux /sys/fs/selinux
    + '[' -d /sys/fs/selinux -a -f /sys/fs/selinux/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ']'
    ++ uname -s
    + '[' Darwin == GNU/kFreeBSD ']'
    + rm -f ./runtime/runtime_defs.go
    + echo '# Building C bootstrap tool.'
    # Building C bootstrap tool.
    + echo cmd/dist
    cmd/dist
    ++ cd ..
    ++ pwd
    + export GOROOT=/Users/ym/work/go1.4
    + GOROOT=/Users/ym/work/go1.4
    + GOROOT_FINAL=/Users/ym/work/go1.4
    + DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    + mflag=
    + case "$GOHOSTARCH" in
    ++ uname
    + '[' Darwin == Darwin ']'
    + mflag=' -mmacosx-version-min=10.6'
    ++ type -t gcc
    ++ type -t clang
    + '[' -z '' -a -z file -a -n file ']'
    + gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c
    ++ ./cmd/dist/dist env -p
    + eval 'CC="clang"' 'CC_FOR_TARGET="clang"' 'GOROOT="/Users/ym/work/go1.4"' 'GOBIN="/Users/ym/me/go/test/bin"' 'GOARCH="amd64"' 'GOOS="darwin"' 'GOHOSTARCH="amd64"' 'GOHOSTOS="darwin"' 'GOTOOLDIR="/Users/ym/work/go1.4/pkg/tool/darwin_amd64"' 'GOCHAR="6"' 'PATH="/Users/ym/me/go/test/bin:/usr/local/opt/openssl@1.1/bin:/usr/local/opt/postgresql@12/bin:/Users/ym/work/go1.4/bin:/usr/local/Homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/ym/mybin/apache-maven-3.6.0/bin:/Users/ym/me/go/test/bin:/Users/ym/.rvm/bin:/Users/ym/mybin/spark-2.4.3-bin-hadoop2.7/bin/"'
    ++ CC=clang
    ++ CC_FOR_TARGET=clang
    ++ GOROOT=/Users/ym/work/go1.4
    ++ GOBIN=/Users/ym/me/go/test/bin
    ++ GOARCH=amd64
    ++ GOOS=darwin
    ++ GOHOSTARCH=amd64
    ++ GOHOSTOS=darwin
    ++ GOTOOLDIR=/Users/ym/work/go1.4/pkg/tool/darwin_amd64
    ++ GOCHAR=6
    ++ PATH='/Users/ym/me/go/test/bin:/usr/local/opt/openssl@1.1/bin:/usr/local/opt/postgresql@12/bin:/Users/ym/work/go1.4/bin:/usr/local/Homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/ym/mybin/apache-maven-3.6.0/bin:/Users/ym/me/go/test/bin:/Users/ym/.rvm/bin:/Users/ym/mybin/spark-2.4.3-bin-hadoop2.7/bin/'
    + '[' '' = true ']'
    + echo

    + '[' '' = --dist-tool ']'
    + echo '# Building compilers and Go bootstrap tool for host, darwin/amd64.'
    # Building compilers and Go bootstrap tool for host, darwin/amd64.
    + buildall=-a
    + '[' '' = --no-clean ']'
    + ./cmd/dist/dist bootstrap -a -v
    lib9
    libbio
    liblink
    cmd/cc
    cmd/gc
    cmd/6l
    /Users/ym/work/go1.4/src/cmd/6l/../ld/dwarf.c:1479:15: warning: implicit conversion from 'int' to 'char' changes value from 156 to -100 [-Wconstant-conversion]
    /Users/ym/work/go1.4/src/cmd/6l/../ld/dwarf.c:1763:21: warning: implicit conversion from 'int' to 'char' changes value from 144 to -112 [-Wconstant-conversion]
    /Users/ym/work/go1.4/src/cmd/6l/../ld/lib.h:168:13: note: expanded from macro 'cput'
    cmd/6a
    cmd/6c
    /Users/ym/work/go1.4/src/cmd/6c/txt.c:995:28: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
    /Users/ym/work/go1.4/src/cmd/6c/txt.c:1045:28: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
    cmd/6g
    /Users/ym/work/go1.4/src/cmd/6g/peep.c:771:13: warning: converting the enum constant to a boolean [-Wint-in-bool-context]
    runtime
    errors
    sync/atomic
    sync
    io
    unicode
    unicode/utf8
    unicode/utf16
    bytes
    math
    strings
    strconv
    bufio
    sort
    container/heap
    encoding/base64
    syscall
    time
    os
    reflect
    fmt
    encoding
    encoding/json
    flag
    path/filepath
    path
    io/ioutil
    log
    regexp/syntax
    regexp
    go/token
    go/scanner
    go/ast
    go/parser
    os/exec
    os/signal
    net/url
    text/template/parse
    text/template
    go/doc
    go/build
    cmd/go
    + cp cmd/dist/dist /Users/ym/work/go1.4/pkg/tool/darwin_amd64/dist
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap clean -i std
    + echo

    + '[' amd64 '!=' amd64 -o darwin '!=' darwin ']'
    + echo '# Building packages and commands for darwin/amd64.'
    # Building packages and commands for darwin/amd64.
    + CC=clang
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap install -ccflags '' -gcflags '' -ldflags '' -v std
    runtime
    errors
    sync/atomic
    unicode
    unicode/utf8
    math
    sort
    encoding
    unicode/utf16
    container/list
    sync
    crypto/subtle
    container/ring
    image/color
    runtime/race
    container/heap
    io
    syscall
    image/color/palette
    hash
    crypto/cipher
    hash/crc32
    crypto/hmac
    hash/adler32
    hash/crc64
    hash/fnv
    bytes
    strings
    bufio
    text/tabwriter
    path
    html
    compress/bzip2
    time
    strconv
    math/rand
    math/cmplx
    os
    reflect
    regexp/syntax
    crypto
    encoding/base64
    net/url
    crypto/aes
    crypto/rc4
    crypto/md5
    crypto/sha1
    crypto/sha256
    crypto/sha512
    encoding/pem
    encoding/ascii85
    encoding/base32
    image
    path/filepath
    net
    os/signal
    io/ioutil
    os/exec
    regexp
    image/draw
    image/jpeg
    fmt
    encoding/binary
    cmd/pprof/internal/svg
    crypto/des
    index/suffixarray
    cmd/internal/goobj
    cmd/internal/rsc.io/arm/armasm
    cmd/internal/rsc.io/x86/x86asm
    debug/dwarf
    debug/gosym
    debug/plan9obj
    flag
    log
    go/token
    encoding/json
    encoding/xml
    text/template/parse
    go/scanner
    debug/elf
    debug/macho
    debug/pe
    go/ast
    compress/flate
    text/template
    math/big
    encoding/hex
    mime
    net/textproto
    cmd/internal/objfile
    net/http/internal
    compress/gzip
    runtime/pprof
    cmd/pack
    cmd/pprof/internal/profile
    cmd/pprof/internal/tempfile
    archive/tar
    archive/zip
    cmd/addr2line
    cmd/nm
    crypto/elliptic
    encoding/asn1
    crypto/rand
    go/parser
    go/printer
    go/doc
    crypto/ecdsa
    crypto/rsa
    crypto/dsa
    crypto/x509/pkix
    mime/multipart
    cmd/objdump
    cmd/pprof/internal/plugin
    crypto/x509
    html/template
    go/build
    cmd/cgo
    go/format
    cmd/fix
    cmd/gofmt
    crypto/tls
    cmd/pprof/internal/symbolizer
    cmd/pprof/internal/symbolz
    cmd/pprof/internal/report
    cmd/yacc
    compress/lzw
    compress/zlib
    database/sql/driver
    database/sql
    encoding/csv
    encoding/gob
    cmd/pprof/internal/commands
    image/gif
    cmd/pprof/internal/driver
    image/png
    log/syslog
    net/mail
    os/user
    runtime/debug
    testing
    testing/iotest
    net/http
    net/smtp
    testing/quick
    text/scanner
    cmd/go
    cmd/pprof/internal/fetch
    expvar
    net/http/cgi
    net/http/cookiejar
    net/http/httptest
    net/http/httputil
    net/http/pprof
    cmd/pprof
    net/rpc
    net/http/fcgi
    net/rpc/jsonrpc
    + echo

    + rm -f /Users/ym/work/go1.4/pkg/tool/darwin_amd64/go_bootstrap
    + '[' '' '!=' --no-banner ']'
    + /Users/ym/work/go1.4/pkg/tool/darwin_amd64/dist banner

    ---
    Installed Go for darwin/amd64 in /Users/ym/work/go1.4
    Installed commands in /Users/ym/me/go/test/bin
    - -

    我们看到15-33行, 首先编译了cmd/dist文件夹下的c代码,生成了一个dist可执行文件,并调用了 /cmd/dist/dist env -p

    -
    -# Building C bootstrap tool.
    -+ echo cmd/dist
    -cmd/dist
    -++ cd ..
    -++ pwd
    -+ export GOROOT=/Users/ym/work/go1.4
    -+ GOROOT=/Users/ym/work/go1.4
    -+ GOROOT_FINAL=/Users/ym/work/go1.4
    -+ DEFGOROOT='-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    -+ mflag=
    -+ case "$GOHOSTARCH" in
    -++ uname
    -+ '[' Darwin == Darwin ']'
    -+ mflag=' -mmacosx-version-min=10.6'
    -++ type -t gcc
    -++ type -t clang
    -+ '[' -z '' -a -z file -a -n file ']'
    -+ gcc -mmacosx-version-min=10.6 -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/Users/ym/work/go1.4"' cmd/dist/arm.c cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/plan9.c cmd/dist/unix.c cmd/dist/windows.c 
    -++ ./cmd/dist/dist env -p
    -
    - -

    那么我们来看看怎么在CLion中调试/cmd/dist/dist env -p

    -

    CLion调试cmd/dist

    如果要用CLion直接打开cmd/dist文件夹,会提示创建cmake项目,但是我们想直接使用调试Makefile项目。 -那么我们在cmd/dist目录下创建一个Makefile文件,内容如下

    -
    CFLAGS = -mmacosx-version-min=10.6 -g -Wall -Werror '-DGOROOT_FINAL="/Users/ym/work/go1.4"'
    CC = gcc

    SRC=arm.c buf.c build.c buildgc.c buildgo.c buildruntime.c main.c plan9.c unix.c windows.c
    INCLUDE_DIR = ./


    TARGET = dist

    all: $(TARGET)

    $(TARGET):
    $(CC) $(CFLAGS) -I$(INCLUDE_DIR) $(SRC) -o $@


    clean:
    rm $(TARGET)

    .PHONY: all clean
    - -

    其实就是根据make.bash打印的gcc编译命令改写的。需要注意的是要增加-g选项,这样才能调试。

    -

    然后参考Run/Debug Configuration: Makefile Application

    -

    配置几个参数,核心要指定的就是Target和Executable文件 -clion-debug-makefile-config

    -

    最后先clean,然后断点调试unix.c文件中的main入口函数。

    -

    clion-makefile-debug-show

    -

    成功了。

    -]]>
    - - debug - clion - makefile - -
    [leetcode 390]Elimination Game原创解法 /2022/02/06/2022-2-6-leetcode-390/ @@ -2980,68 +2980,6 @@ POST_BUILD 事件的触发是PicPicker链接完成,而不是复制文件到Pic bundle - - MacOS下LLDB调试Qt5程序 - /2023/06/09/2023-6-9-qt5-macos-lldb-debug/ - LLDB调试Qt5的问题

    MacOS下,LLDB调试Qt5应用通常会遇到无法打印QString变量的问题。

    -
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003a81 a.out`main(argc=1, argv=0x00007ff7bfefeb40) at test.cpp:10:11
    7 QCoreApplication a(argc, argv);
    8
    9 QString s = "Hello World";
    -> 10 return a.exec();
    11 }
    Target 0: (a.out) stopped.
    (lldb) print s
    (QString) $0 = {
    d = 0x0000600000c04b70
    }
    - -

    这里无法获取变量s的值Hello World,只能看到一个地址。

    - - -

    同样的,使用CLion等IDE使用LLDB调试Qt5应用的时候,也无法看到值。

    -

    LLDB Qt Formatter

    英雄来了, https://github.com/ayuckhulk/lldb-qt-formatters

    -
    -

    I use Xcode and LLDB to debug my Qt programs, and got tired with there being no visualisation for all the built-in types. Here I endeavour to make all of these types visible through the debugger. Works with Qt 5.x. Tested with Qt 5.9.8, 5.13.2 and XCode 11.

    -
    -

    作者使用Xcode和LLDB开发Qt,苦于debug的时候看不到qt的内建类型的值。

    -

    怎么办呢,简单的说就是LLDB启动的时候,加载python脚本,把内建类型的显示值的计算方法替换掉。也就是LLDB可以加载这样的自定义Formatter, -对变量进行格式化输出 —— Variable Formatting, -Python脚本非常适合实现复杂的Formatter —— Python Scripting

    -

    安装方法如下:

    -
    -

    git clone this repo somewhere, e.g. ~/qtlldb. Then add the following lines to your ~/.lldbinit:

    -

    command script import ~/qtlldb/QtFormatters.py
    -command source ~/qtlldb/QtFormatters.lldb

    -
    -

    这样,Qt的如下内建类型的值都可以在调试的时候可视化了。

    -
      -
    • QString
    • -
    • QUrl
    • -
    • QList
    • -
    • QVector
    • -
    • QPointer
    • -
    • QSize
    • -
    • QSizeF
    • -
    • QPoint
    • -
    • QPointF
    • -
    • QRect
    • -
    • QRectF
    • -
    • QUuid
    • -
    -

    测试

    还用如下test.cpp代码测试,仅仅测试QString类型。

    -
    #include <QCoreApplication>
    #include <QString>


    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    QString s = "Hello World";
    return a.exec();
    }
    - -

    编译一下

    -
    g++ -std=c++11 test.cpp $(pkg-config --cflags --libs Qt5Core) -g
    - -

    这里需要设置PKG_CONFIG_PATH一下

    -
    export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/"
    - -

    然后LLDB调试a.out。

    -
    $ lldb a.out
    (lldb) target create "a.out"
    Current executable set to '/Users/ym/tmp/lldb-qt-formatters/lldbtests/a.out' (x86_64).
    (lldb) b 10
    Breakpoint 1: where = a.out`main + 65 at test.cpp:10:11, address = 0x0000000100003a81
    (lldb) r
    Process 53042 launched: '/Users/ym/tmp/lldb-qt-formatters/lldbtests/a.out' (x86_64)
    Process 53042 stopped
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003a81 a.out`main(argc=1, argv=0x00007ff7bfefeb40) at test.cpp:10:11
    7 QCoreApplication a(argc, argv);
    8
    9 QString s = "Hello World";
    -> 10 return a.exec();
    11 }
    Target 0: (a.out) stopped.
    (lldb) print s
    (QString) $0 = "Hello World" {
    d = 0x0000600000c003f0
    }
    (lldb)
    - -

    这里18行”Hello World”显示出来了。

    -

    总结

    对LLDB启动进行设置之后,CLion里也可以愉快的调试了。

    -

    lldb-debug-qt

    -]]>
    - - debug - qt5 - macos - lldb - -
    玩一下LangChain /2023/06/08/2023-6-8-langchain-openai/ @@ -3168,6 +3106,68 @@ command source ~/qtlldb/QtFormatters.lldb

    tree
    + + MacOS下LLDB调试Qt5程序 + /2023/06/09/2023-6-9-qt5-macos-lldb-debug/ + LLDB调试Qt5的问题

    MacOS下,LLDB调试Qt5应用通常会遇到无法打印QString变量的问题。

    +
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003a81 a.out`main(argc=1, argv=0x00007ff7bfefeb40) at test.cpp:10:11
    7 QCoreApplication a(argc, argv);
    8
    9 QString s = "Hello World";
    -> 10 return a.exec();
    11 }
    Target 0: (a.out) stopped.
    (lldb) print s
    (QString) $0 = {
    d = 0x0000600000c04b70
    }
    + +

    这里无法获取变量s的值Hello World,只能看到一个地址。

    + + +

    同样的,使用CLion等IDE使用LLDB调试Qt5应用的时候,也无法看到值。

    +

    LLDB Qt Formatter

    英雄来了, https://github.com/ayuckhulk/lldb-qt-formatters

    +
    +

    I use Xcode and LLDB to debug my Qt programs, and got tired with there being no visualisation for all the built-in types. Here I endeavour to make all of these types visible through the debugger. Works with Qt 5.x. Tested with Qt 5.9.8, 5.13.2 and XCode 11.

    +
    +

    作者使用Xcode和LLDB开发Qt,苦于debug的时候看不到qt的内建类型的值。

    +

    怎么办呢,简单的说就是LLDB启动的时候,加载python脚本,把内建类型的显示值的计算方法替换掉。也就是LLDB可以加载这样的自定义Formatter, +对变量进行格式化输出 —— Variable Formatting, +Python脚本非常适合实现复杂的Formatter —— Python Scripting

    +

    安装方法如下:

    +
    +

    git clone this repo somewhere, e.g. ~/qtlldb. Then add the following lines to your ~/.lldbinit:

    +

    command script import ~/qtlldb/QtFormatters.py
    +command source ~/qtlldb/QtFormatters.lldb

    +
    +

    这样,Qt的如下内建类型的值都可以在调试的时候可视化了。

    +
      +
    • QString
    • +
    • QUrl
    • +
    • QList
    • +
    • QVector
    • +
    • QPointer
    • +
    • QSize
    • +
    • QSizeF
    • +
    • QPoint
    • +
    • QPointF
    • +
    • QRect
    • +
    • QRectF
    • +
    • QUuid
    • +
    +

    测试

    还用如下test.cpp代码测试,仅仅测试QString类型。

    +
    #include <QCoreApplication>
    #include <QString>


    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    QString s = "Hello World";
    return a.exec();
    }
    + +

    编译一下

    +
    g++ -std=c++11 test.cpp $(pkg-config --cflags --libs Qt5Core) -g
    + +

    这里需要设置PKG_CONFIG_PATH一下

    +
    export PKG_CONFIG_PATH="/usr/local/opt/qt@5/lib/"
    + +

    然后LLDB调试a.out。

    +
    $ lldb a.out
    (lldb) target create "a.out"
    Current executable set to '/Users/ym/tmp/lldb-qt-formatters/lldbtests/a.out' (x86_64).
    (lldb) b 10
    Breakpoint 1: where = a.out`main + 65 at test.cpp:10:11, address = 0x0000000100003a81
    (lldb) r
    Process 53042 launched: '/Users/ym/tmp/lldb-qt-formatters/lldbtests/a.out' (x86_64)
    Process 53042 stopped
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003a81 a.out`main(argc=1, argv=0x00007ff7bfefeb40) at test.cpp:10:11
    7 QCoreApplication a(argc, argv);
    8
    9 QString s = "Hello World";
    -> 10 return a.exec();
    11 }
    Target 0: (a.out) stopped.
    (lldb) print s
    (QString) $0 = "Hello World" {
    d = 0x0000600000c003f0
    }
    (lldb)
    + +

    这里18行”Hello World”显示出来了。

    +

    总结

    对LLDB启动进行设置之后,CLion里也可以愉快的调试了。

    +

    lldb-debug-qt

    +]]>
    + + debug + qt5 + macos + lldb + +
    BitTorrent协议(三)之磁力链接获取元数据 /2019/01/21/2019-1-21-bt-3/ diff --git a/sitemap.xml b/sitemap.xml index 97c14f9c..8e52b2c7 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -38,7 +38,7 @@ - https://threelambda.com/2023/06/09/2023-6-9-qt5-macos-lldb-debug/ + https://threelambda.com/2023/06/08/2023-6-8-langchain-openai/ 2024-07-22 @@ -47,7 +47,7 @@ - https://threelambda.com/2023/06/08/2023-6-8-langchain-openai/ + https://threelambda.com/2023/06/09/2023-6-9-qt5-macos-lldb-debug/ 2024-07-22 @@ -119,7 +119,7 @@ - https://threelambda.com/2020/09/08/remote-debug-with-clion/ + https://threelambda.com/2020/10/09/x509-ca/ 2024-07-22 @@ -128,7 +128,7 @@ - https://threelambda.com/2020/10/09/x509-ca/ + https://threelambda.com/2020/09/08/remote-debug-with-clion/ 2024-07-22 @@ -281,7 +281,7 @@ - https://threelambda.com/2018/11/09/minilisp/ + https://threelambda.com/2017/07/09/2017-7-9-resize-fixed-image/ 2024-07-22 @@ -290,7 +290,7 @@ - https://threelambda.com/2017/07/09/2017-7-9-resize-fixed-image/ + https://threelambda.com/2018/11/09/minilisp/ 2024-07-22 @@ -299,7 +299,7 @@ - https://threelambda.com/2017/03/06/2017-3-6-grails-call-java/ + https://threelambda.com/2017/05/19/2017-5-19-javacc-minilisp/ 2024-07-22 @@ -308,7 +308,7 @@ - https://threelambda.com/2017/05/19/2017-5-19-javacc-minilisp/ + https://threelambda.com/2017/03/06/2017-3-6-grails-call-java/ 2024-07-22 @@ -380,7 +380,7 @@ - https://threelambda.com/2016/09/27/2016-9-27-leetcode-200/ + https://threelambda.com/2016/12/31/leetcode-327/ 2024-07-22 @@ -398,7 +398,7 @@ - https://threelambda.com/2016/12/31/leetcode-327/ + https://threelambda.com/2016/09/27/2016-9-27-leetcode-200/ 2024-07-22 @@ -407,7 +407,7 @@ - https://threelambda.com/2016/12/21/get-val-name-and-value/ + https://threelambda.com/2016/12/22/pandas-read-csv/ 2024-07-22 @@ -416,7 +416,7 @@ - https://threelambda.com/2016/12/22/pandas-read-csv/ + https://threelambda.com/2016/12/21/get-val-name-and-value/ 2024-07-22 @@ -473,14 +473,14 @@ - https://threelambda.com/tags/R/ + https://threelambda.com/tags/%E6%8A%80%E5%B7%A7/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/%E6%8A%80%E5%B7%A7/ + https://threelambda.com/tags/R/ 2024-07-22 weekly 0.2 @@ -536,63 +536,63 @@ - https://threelambda.com/tags/grails/ + https://threelambda.com/tags/javacc/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/gradle/ + https://threelambda.com/tags/lisp/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/javacc/ + https://threelambda.com/tags/gradle/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/lisp/ + https://threelambda.com/tags/build/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/build/ + https://threelambda.com/tags/grails/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/macro/ + https://threelambda.com/tags/image/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/image/ + https://threelambda.com/tags/resize/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/resize/ + https://threelambda.com/tags/virutalbox/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/virutalbox/ + https://threelambda.com/tags/macro/ 2024-07-22 weekly 0.2 @@ -711,63 +711,63 @@ - https://threelambda.com/tags/debug/ + https://threelambda.com/tags/certificate/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/CLion/ + https://threelambda.com/tags/X-509/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/gdb/ + https://threelambda.com/tags/nginx/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/remote/ + https://threelambda.com/tags/openssl/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/certificate/ + https://threelambda.com/tags/ssl/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/X-509/ + https://threelambda.com/tags/debug/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/nginx/ + https://threelambda.com/tags/CLion/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/openssl/ + https://threelambda.com/tags/gdb/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/ssl/ + https://threelambda.com/tags/remote/ 2024-07-22 weekly 0.2 @@ -781,28 +781,28 @@ - https://threelambda.com/tags/pod/ + https://threelambda.com/tags/clion/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/coredns/ + https://threelambda.com/tags/makefile/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/clion/ + https://threelambda.com/tags/pod/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/makefile/ + https://threelambda.com/tags/coredns/ 2024-07-22 weekly 0.2 @@ -865,42 +865,42 @@ - https://threelambda.com/tags/lldb/ + https://threelambda.com/tags/langchain/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/langchain/ + https://threelambda.com/tags/openai/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/openai/ + https://threelambda.com/tags/c/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/c/ + https://threelambda.com/tags/AST/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/AST/ + https://threelambda.com/tags/tree/ 2024-07-22 weekly 0.2 - https://threelambda.com/tags/tree/ + https://threelambda.com/tags/lldb/ 2024-07-22 weekly 0.2